我对幻影类型的使用感到困惑:
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
。
然而这编译好吗?我做错了什么?
答案 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