Haskell 带有记录更新语法的定义不明确
例如:Haskell 带有记录更新语法的定义不明确,haskell,Haskell,例如: 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 :: F
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(可能是真正的疯狂)
,尽管我在乎。我正在将其覆盖到fooint
(或者必须是这样开始的)
我想:
defFoo :: Foo Int
defFoo = def
这是一个解决办法,但是。。。很难看。有更好的方法吗?我缺少一些语言扩展?看来您已经找到了问题的根源。之后剩下的就是美学 我可以理解对另一个全局绑定的偏见。本地的怎么样
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
}
并将其与已编译的代码相链接。类型类实例是全局的(“开放世界”假设),因此它们的实例可供代码使用
所以现在,对于一些a
,当给定def::Foo a
时,有两种可能性:
,这意味着默认值a
fooName def==“foo”
,这意味着a~MyCustomDataType
fooName def==“bar”
如果你想关闭这个世界-防止像这样的模糊性被引入,你必须告诉ghc怎么做。不知怎的,我曾期望
\s->s{fooId=4::Int}
是fooint->fooint
类型,而实际上它是更一般的fooa->fooint
。我想知道这个“特性”在实际代码中被利用的频率有多高——现在我只能指出它阻止类型推断的地方,就像上面的代码一样。我想我确实忽略了一个事实,即可以用不同的值定义一个特定于给定类型的默认实例,尽管已经定义了fooa
的默认实例。有道理。说得好,谢谢:)