Reputation: 62808
I'm working on a simple compiler. It starts off with a complicated data structure which is recursive (i.e., it's an expression tree), and ends up with exactly the same data structure, but with recursion disallowed.
"No problem", I thought to myself, "I'll just make the recursion type a type parameter, and then define type synonyms for the recursive and non-recursive types".
...ah, yes, but it turns out you can't actually do that. You see, type synonyms are not allowed to be recursive. >facepalm<
Any idea how I can achieve what I'm after?
Here's a very simplified example. Suppose I have these two data types:
data Type1 = Foo ID | Bar [Type1] | Baz Type1 Type1
data Type2 = Foo ID | Bar [ID] | Baz ID ID
My plan was to simply do this:
data Type r = Foo ID | Bar [r] | Baz r r
type Type1 = Type Type1
type Type2 = Type ID
But obviously that doesn't actually work.
Currently, it appears my options are these:
Neither of these is really satisfactory. For the trivial example above, duplicating the type isn't too bad; for a big type definition, you do not want to be doing this!
Upvotes: 1
Views: 84
Reputation: 63349
This sound like you might use recursion-schemes. You basically have two options:
Use Fix
(or Mu
or Nu
) for creating the recursive variant, which means some nuisance with (un)wrapping its constructor all the time.
data TypeF r = FooF ID | BarF [r] | BazF r r
deriving (Functor)
type Type1 = Fix TypeF
type Type2 = TypeF ID
Define your own variant for the recursive case and implement project
/embed
as needed:
data Type = Foo ID | Bar [Type] | Baz Type Type
type instance Base Type = TypeF
instance Foldable Type where
project (Foo ID) = FooF ID
project (Bar xs) = BarF xs
project (Baz x y) = BazF x y
instance Unfoldable Type where
embed (FooF ID) = Foo ID
embed (BarF xs) = Bar xs
embed (BazF x y) = Baz x y
The type family Base
provides the connection between the two: project
and embed
witness the isomorphism between Type
and Base Type Type
, and Base Type Type
is equivalent to TypeF Type
, unwrapping one level of recursion.
In both cases Foldable
and Unfoldable
implement various *-morphisms for converting between the recursive and non-recursive representations.
Upvotes: 2
Reputation: 170713
You can do this:
data Type r = Foo ID | Bar [r] | Baz r r
newtype Type1 = Type1 (Type Type1)
newtype Type2 = Type2 (Type ID) -- or type Type2 = Type ID, but this is more consistent
You need to remove an extra type constructor in functions dealing with Type1
, but this shouldn't be a major problem.
More generally, you can define a fixed-point type constructor and use it for defining Type1
.
Upvotes: 2