我是Haskell初学者在Haskell中为一些简单的语言编写玩具编译器,并且在完成解析器之后,我开始研究第一个“阶段”(类型检查,一些简化,代码生成等)。我的AST数据类型如下所示:
data Ast a = ConstInt Int a | Variable String a | SomeComplexThing .... a
关键是,我希望在每次传递后使用a
槽来存储信息:比如,在解析之后我们有Ast Position
,然后在类型检查之后我们有Ast (Position,TypeInfo)
等等等等。
但是,我希望在舞台上独立访问此有效负载中的值。截至目前,在不同阶段,元组属于不同类型,因此没有统一的自动方式来实现这一点。如果我要移除一个舞台,一切都要改变。
所以,我心中的haskell noob想到了:Typeclasses!
现在,我可以轻松制作TypeInfoHolder
类型类和PositionHolder
类型类,依此类推
class TypeInfoHolder a where
getTypeInfo::a->TypeInfo
并有一个
data TypeInfoHolderObj a = TypeInfoHolderObj a TypeInfo
并将其设为TypeInfoHolder
的实例。
现在我想以某种方式自动确保每个TypeInfoHolderObj a
也是a
所属的所有类的实例,也就是每个类型A
a
的实例。 } implements,然后TypeInfoHolderObj a
也通过将类的函数应用于a
TypeInfoHolderObj
成员来实现
我只是想出于好奇而知道这件事:看起来这种事情经常会出现。
答案 0 :(得分:6)
你似乎有两个问题。
使用类型类提供从多个其他类型中投射某种类型的能力是一个好主意吗?和,
有没有办法让像TypeInfoHolderObj a
这样的类型继承a
的所有实例
对(1)的回答有点争议,因为"对"对类型类的使用有点争议。就个人而言,我认为答案是"是的"它在镜头文献中有所支持,其中像HasX
这样的类词组很常见。这绝对是一个风格问题,所以很难得到一个有意义的答案 - 特别是Stack Overflow。
(2)的答案是" no"并且很容易说明原因。仅考虑需要自动派生的许多示例实例中的一个
instance Monoid a => Monoid (TypeInfoHolderObj a) where ...
对于某些类型(可能是newtypes
的类型),此实例派生可以自动完成(请参阅GeneralizedNewtypeDeriving
),但对于TypeInfoHolderObj
,除非TypeInfo
< em>还共享Monoid
实例。如果确实如此,我们可以做到
mempty = TypeInfoHolderObj mempty mempty
mappend (TypeInfoHolderObj a b) (TypeInfoHolderObj c d) =
TypeInfoHolderObj (mappend a c) (mappend b d)
一般而言,我们并不了解自动派生此类实例所需的所有假设。但是,可以猜测它们何时可能发生,因为您可以期望它们在您的实例看起来有点像
时工作class Foo a where ...
instance (Foo a, Foo b) => Foo (a, b) where ...
因为,实际上,TypeInfoHolderObj a
只不过是(a, TypeInfo)
。