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
的默认实例。有道理。说得好,谢谢:)