幻影式混乱?

时间:2016-10-08 10:41:55

标签: haskell phantom-types

我对幻影类型的使用感到困惑:

type Words = String
type Numbers = Int

data NonPhantom = NP1 Words | NP2 Numbers deriving (Show)

data Phantom a = P1 Words | P2 Numbers deriving (Show) 

nonPhantomFunction :: NonPhantom -> Int
nonPhantomFunction r = 100


phantomFunction :: Phantom Numbers -> Int
phantomFunction a = 2001


main = do
   print $ nonPhantomFunction (NP1 "sdsdds") --can also pass NP2 here! 
   print $ phantomFunction (P1 "sdsdsd") --This shouldn't work!?

我希望此代码 NOT 能够进行编译,因为phantomFunction明确说明了Phantom的预期数据类型Numbers

然而这编译好吗?我做错了什么?

3 个答案:

答案 0 :(得分:4)

data Phantom a = P1 Words | P2 Numbers deriving (Show) 

对于任何P1 "aa"Phantom a形成任何类型a的{​​{1}},包括Numbers

答案 1 :(得分:1)

构造函数的参数与构造函数所属类型的类型参数之间没有隐式连接。如果希望type参数表示的类型出现在构造函数的参数中的任何位置,则需要显式声明它。

您也可以在以下表达式中看到这一点:

Nothing
[]

第一个可以为任何Maybe a创建a,第二个可以为任何[a]创建一个列表a

同样地

P1 "xyz"
您的示例中的

可以为Phantom a

制作a

答案 2 :(得分:0)

其他答案已经解释了构造函数的类型

P1 :: Words -> Phantom a

表示能够为 P1 a的任何选项构建a类型的值;特别是,a ~ Numbers的选择。这就是你的函数调用

的原因
phantomFunction (P1 "sdsdsd")

typechecks。

现在,你如何解决这个问题?我想你想要P1 :: Words -> Phantom Words? GADT允许您通过允许编写来约束构造值类型中出现的类型变量

{-# LANGUAGE GADTs #-}
data Phantom a where
    P1 :: Words -> Phantom Words
    P2 :: Numbers -> Phantom Numbers

这将

  • 确保P1 _的类型为Phantom Words,因此您无法使用Phantom Numbers构建
  • 仅使Phantom Words
  • 匹配,允许使用P1的函数进行详尽的模式匹配
  • 允许多态性超过Phantom a的函数根据模式匹配(这是最大的)来细化其类型,因此您可以编写例如。

    dup :: Phantom a -> a
    dup (P1 ws) = ws ++ ws -- Here, we have to return a Words, and ws is a Words, so ++ will work
    dup (P2 n) = n + n -- Here, we have to return a Numbers, and x is a Numbers, so + will work