leo*_*leo 4 haskell typeclass phantom-types
我们有以下数据类型:
data Foo1 a = Foo1
data Foo2 a = Foo2 (Foo3 a)
data Foo3 a = C1 (Foo1 a) | C2 Int
Run Code Online (Sandbox Code Playgroud)
现在我们希望能够从Foo1或Int获得Foo3.解决方案可以是使用类型类:
class ToFoo3 a where
toFoo3 :: a -> Foo3 b -- Here start the problems with this phantom type b...
instance ToFoo3 (Foo1 b) where
toFoo3 foo1 = C1 foo1
instance ToFoo3 Int where
toFoo3 int = C2 int
Run Code Online (Sandbox Code Playgroud)
在这里,编译器抱怨(正确!)它不能将b与b1匹配,因为类定义中的Foo3的"b"与实例中的Foo1的"b"不同.
有办法解决这个问题吗?
没有函数依赖的多参数类型类为我编译:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
data Foo1 a = Foo1
data Foo2 a = Foo2 (Foo3 a)
data Foo3 a = C1 (Foo1 a) | C2 Int
class ToFoo3 a b where
toFoo3 :: a -> Foo3 b
instance ToFoo3 (Foo1 b) b where
toFoo3 foo1 = C1 foo1
instance ToFoo3 Int b where
toFoo3 int = C2 int
Run Code Online (Sandbox Code Playgroud)
我理解它的方式,你不能在任何一个方向上有功能依赖,因为Int需要能够转换为任何Foo3 a类型,并且Foo1 a 还需要能够转换为相同的Foo3 a类型.
当然,这意味着你不能指望任何参数或结果类型toFoo3有助于推断另一个,所以你有时可能需要一些烦人的类型注释来使用它,但除此之外这应该有效.
编辑:我假设你不希望能够从转换Foo1 a到Foo3 b同a和b不同.如果我错了,那么如果你将一个实例更改为,那么带有单参数类的OP代码应该可以工作
instance ToFoo3 (Foo1 b) where
toFoo3 Foo1 = C1 Foo1
Run Code Online (Sandbox Code Playgroud)
哇,其他两种方法很复杂。
简单的解决方案是记住这些是幻像类型,您可以根据需要重建它们。例如,如果存在一个类型的data Phantom x y = Phantom x函数,它使幻像类型再次通用。方法是:cast (Phantom x) = Phantom xcast :: Phantom x y -> Phantom x z
在这种情况下,整个解决方案非常简单:
instance ToFoo3 (Foo1 b) where
toFoo3 _ = C1 Foo1
Run Code Online (Sandbox Code Playgroud)
类似地,对于Foo2和Foo3,这是接下来的逻辑步骤:
instance ToFoo3 (Foo3 a) where
toFoo3 (C1 x) = C1 Foo1
toFoo3 (C2 i) = C2 i
instance ToFoo3 (Foo2 a) where
toFoo3 (Foo2 x) = toFoo3 x
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
492 次 |
| 最近记录: |