示例:
data Foo a
= Foo { fooId :: a
, fooName :: String
, fooCount :: Int
}
instance Default a => Default (Foo a) where
def = Foo
{ fooId = def
, fooName = "foo"
, fooCount = 0
}
-- this is ok
x :: Foo Int
x = def { fooName = "good" }
-- error, type of def is ambiguous (a could be anything with a Default instance)
y :: Foo Int
y = def { fooId = 2 }
-- ok again
z :: Foo Int
z = (def :: Foo Int) { fooId = 2 }
我想我明白为什么它是暧昧的。这不是问题。想象:
fooList :: [Foo Int]
fooList =
[ (def :: Foo Int) { fooId = 0, fooName = "one" }
, (def :: Foo Int) { fooId = 1 }
, (def :: Foo Int) { fooId = 2, fooName = "three", fooCount = 42 }
...
]
相反,我想写:
fooList :: [Foo Int]
fooList =
[ def { fooId = 0, fooName = "one" }
, def { fooId = 1 }
, def { fooId = 2, fooName = "three", fooCount = 42 }
...
]
在这种情况下,GHC如何推断def的类型是否有效?对于我关心的所有人来说可能是Foo (Maybe SomethingReallyCrazy)
。我当时正在覆盖它Foo Int
(或者它必须是那个开头)。
我想定义:
defFoo :: Foo Int
defFoo = def
将是一种解决方法,但......它很难看。有没有更好的方法呢?我缺少一些语言扩展?
答案 0 :(得分:2)
看来你已经找到了问题的根源。之后剩下的就是美学。
我能理解对另一种全球约束的偏见。当地人怎么样?
fooList :: [Foo Int]
fooList = let def = Data.Default.Class.def :: Foo Int in
[ def { fooId = 0, fooName = "one" }
, def { fooId = 1 }
, def { fooId = 2, fooName = "three", fooCount = 42 }
...
]
如果你愿意放弃def
作为唱片名称,你可以减少一点尴尬:
fooList :: [Foo Int]
fooList = let d = def :: Foo Int in
[ d { fooId = 0, fooName = "one" }
, d { fooId = 1 }
, d { fooId = 2, fooName = "three", fooCount = 42 }
...
]
至于这个
在这种情况下,GHC如何推断def的类型是否有效?
确实如此!假设某人还在另一个文件中定义了这个:
data MyCustomDataType = Mwahahaha -- note, has no `Default` instance
instance Default (Foo MyCustomDataType) where
def = Foo
{ fooId = Mwahahaha
, fooName = "bar"
, fooCount = 3
}
并将其与已编译的代码相关联。类型类实例是全局的(“开放世界”假设),因此它们的实例可用于您的代码。
现在,当某些def :: Foo a
被赋予a
时,有两种可能性:
Default a
,暗示fooName def == "foo"
。a ~ MyCustomDataType
,暗示fooName def == "bar"
。这是一个无法解决的歧义。
如果你想要关闭这个世界 - 防止引入这样的歧义,你必须告诉ghc 如何。