我正在研究一个简单的编译器。它从一个复杂的数据结构开始,它是递归的(即,它是一个表达式树),并以完全相同的数据结构结束,但不允许递归。
“没问题”,我心想,“我只是将递归类型设为类型参数,然后为递归和非递归类型定义类型同义词”。
......啊,是的,但事实证明你实际上并不能这样做。你看,类型同义词不允许递归。 >捂脸<
知道如何实现我的目标吗?
这是一个非常简化的例子。假设我有这两种数据类型:
data Type1 = Foo ID | Bar [Type1] | Baz Type1 Type1
data Type2 = Foo ID | Bar [ID] | Baz ID ID
我的计划就是这样做:
data Type r = Foo ID | Bar [r] | Baz r r
type Type1 = Type Type1
type Type2 = Type ID
但显然这实际上并不奏效。
目前看来我的选择是:
这些都不是真的令人满意。对于上面的小例子,复制类型并不算太糟糕;对于大类型定义,您不希望这样做!
答案 0 :(得分:2)
你可以这样做:
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
您需要在处理Type1
的函数中删除一个额外的类型构造函数,但这不应该是一个主要问题。
更一般地说,您可以定义a fixed-point type constructor并将其用于定义Type1
。
答案 1 :(得分:2)
这听起来像你可能会使用recursion-schemes。你基本上有两个选择:
使用Fix
(或Mu
或Nu
)创建递归变体,这意味着(un)一直包装其构造函数会产生一些麻烦。
data TypeF r = FooF ID | BarF [r] | BazF r r
deriving (Functor)
type Type1 = Fix TypeF
type Type2 = TypeF ID
为递归案例定义您自己的变体,并根据需要实施project
/ embed
:
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
类型系列Base
提供了两者之间的联系:project
和embed
见证Type
和Base Type Type
之间的同构,Base Type Type
}等于TypeF Type
,展开一级递归。
在这两种情况下Foldable
和Unfoldable
实现了各种* -morphisms,用于在递归和非递归表示之间进行转换。