Skip to content

Instantly share code, notes, and snippets.

@nuttycom
Last active August 29, 2015 14:19
Show Gist options
  • Save nuttycom/24b666c56f5631ed89f9 to your computer and use it in GitHub Desktop.
Save nuttycom/24b666c56f5631ed89f9 to your computer and use it in GitHub Desktop.
Overlapping F-algebras in Haskell

On a number of occasions, I've encountered situations where I've wanted to be able to define multiple data types that referenced the same data constructors, without requiring wrapping and unwrapping; while lenses make traversing these sorts of nested data structures easy, I still am not completely comfortable with them, so I went looking for something a bit simpler. The solution that came to me was this:

data X = X
data Y = Y
data Z = Z

-- An algebra over X and Y
data XYAlg a = XYAlg (X -> a) (Y -> a)

class XYEval b where
  evalXY :: XYAlg a -> b -> a

instance XYEval X where
  evalXY (XYAlg f _) = f

instance XYEval Y where
  evalXY (XYAlg _ f) = f

-- An algebra that overlaps with XYAlg in Y
data YZAlg a = YZAlg (Y -> a) (Z -> a)

class YZEval b where
  evalYZ :: YZAlg a -> b -> a

instance YZEval Y where
  evalYZ (YZAlg f _) = f

instance YZEval Z where
  evalYZ (YZAlg _ f) = f

Now, I can write code that's constrained by XYEval or YZEval. The only downside that I can see here is that if I want to pass around mixtures of Xs, Ys and Zs that I'll have to create data wrappers that capture the typeclass instances for their evaluators via an existential:

{-# LANGUAGE ExistentialQuantification #-}

data XY a = XYEval a => XY a
data YZ a = YZEval a => YZ a

Too much effort for overlapping data constructors? I'm not sure. The one nice thing about this approach is that it allows one to work in a bit more ad-hoc of fashion than do standard data declarations, but are there other approaches that are less noisy and/or avoid the existential?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment