如何为幻像类型创建实例返回幻像类型?

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"不同.

有办法解决这个问题吗?

Ørj*_*sen 6

没有函数依赖的多参数类型类为我编译:

{-# 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 aFoo3 bab不同.如果我错了,那么如果你将一个实例更改为,那么带有单参数类的OP代码应该可以工作

instance ToFoo3 (Foo1 b) where
    toFoo3 Foo1 = C1 Foo1
Run Code Online (Sandbox Code Playgroud)


CR *_*ost 5

哇,其他两种方法很复杂。

简单的解决方案是记住这些是幻像类型,您可以根据需要重建它们。例如,如果存在一个类型的data Phantom x y = Phantom x函数,它使幻像类型再次通用。方法是:cast (Phantom x) = Phantom xcast :: Phantom x y -> Phantom x z

  1. 将对象解构为其非幻像参数。
  2. 重建对象。
  3. 利润。

在这种情况下,整个解决方案非常简单:

instance ToFoo3 (Foo1 b) where
    toFoo3 _ = C1 Foo1
Run Code Online (Sandbox Code Playgroud)

类似地,对于Foo2Foo3,这是接下来的逻辑步骤:

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)