【问题标题】:Haskell deduced too strict typeHaskell 推断出过于严格的类型
【发布时间】:2015-09-18 22:57:23
【问题描述】:

免责声明:我才刚开始学习 Haskell,我不确定“严格”在这里是否合适。

我试图缩小我的问题范围,但我无法真正找到问题,所以这是我的代码,无法编译

module Json where
import Data.List (intersperse)

data JNode = 
    JObject [(String, JNode)]
  | JArray  [JNode]
  | JString String
  | JNumber Double
  | JBool   Bool
  | JNull

instance Show JNode where
  show = show_node 0 where
    glue = foldl (++) ""
    show_tabs n              = glue $ take n $ repeat "  "
    show_list n              = glue . intersperse ",\n" . map (show_pair (n + 1))
    show_sect n l r xs       = glue ["\n", tabs, l, "\n", show_list n xs, "\n", tabs, r] where tabs = show_tabs n
    -- show_pair :: (Show a) => Int -> (a, JNode) -> String -- works when uncommented
    show_pair n (name,  val) = glue [show_tabs n, show name, " : ", show_node n val]
    show_node n (JObject xs) = show_sect n "{" "}" xs
    show_node n (JArray  xs) = show_sect n "[" "]" $ zip [0..] xs
    show_node n (JString x ) = show x
    show_node n (JNumber x ) = show x
    show_node n (JBool   x ) = show x
    show_node n (JNull     ) = "null"

错误是:

Prelude> :l scripts\json.hs
[1 of 1] Compiling Json             ( scripts\json.hs, interpreted )

scripts\json.hs:21:59:
    No instance for (Enum String)
      arising from the arithmetic sequence `0 .. '
    In the first argument of `zip', namely `([0 .. ])'
    In the second argument of `($)', namely `zip ([0 .. ]) xs'
    In the expression: show_sect n "[" "]" $ zip ([0 .. ]) xs

scripts\json.hs:21:60:
    No instance for (Num String) arising from the literal `0'
    In the expression: 0
    In the first argument of `zip', namely `[0 .. ]'
    In the second argument of `($)', namely `zip [0 .. ] xs'
Failed, modules loaded: none.

看一下有注释的那行代码。显然,当没有类型声明时,它需要我传递String 而不仅仅是Show a。有趣的是,当我什至不使用它时,它仍然需要 name 成为 String,例如当用这个替换show_pair 实现时:

show_pair n (name,  val) = show_node n val

有人可以向我解释为什么它会这样工作吗?


简化版我的代码有同样的问题,以防有人要改进答案:

data TFoo = 
    FooStr (String, TFoo)
  | FooNum (Int,    TFoo)

-- show_pair :: (a, TFoo) -> String
show_pair (_,    val) = show_node val
show_node (FooStr  x) = show_pair x
show_node (FooNum  x) = show_pair x

【问题讨论】:

  • snake_case 在 Haskell 中不常用,camelCase 是最遵循的命名方式。
  • 我强烈建议将所有这些本地函数分解到顶层并给每个函数一个类型签名。你不需要从你的模块中导出它们,但是整个混乱会更容易阅读和使用。
  • 更一般地说,当你遇到类型错误并且不知道问题出在哪里时,添加一些类型签名通常会帮助你获得更多有用的错误消息。在某些情况下,ScopedTypeVariables 扩展对于为本地绑定提供类型签名是必要的。我不认为这里是这种情况,但我怀疑你的功能无论如何都会更好地分解。
  • 我在编写 Haskell 时学到的一件事(这反映了上面的评论):如有疑问,请添加签名!准确地告诉它你想限制它的猜测。大多数时候它可能是很好的猜测类型,但给予它帮助总是会让你受益!
  • 顺便说一句,foldl (++) "" 是一种非常低效的连接字符串的方法。快速的方法是foldr (++) "",或者,推广到列表,foldr (++) [],更好地称为concat

标签: haskell type-inference


【解决方案1】:

tl;dr:当您希望某物具有多态性时,请始终使用显式签名。


System F(这是 Haskell 的类型系统所基于的)中,用于多态的每个类型变量都需要通过类型级别的 lambda / for-all (∀) 显式量化到范围内。所以实际上你需要有

show_pair :: ∀ a. (Show a) => Int -> (a, JNode) -> String

可以说 Haskell 也应该要求这个,但事实并非如此。它只是量化您在显式签名中提到的任何变量,因此您可以简单地编写

show_pair :: (Show a) => Int -> (a, JNode) -> String

此外,它尝试将尽可能多的类型变量引入顶级绑定,而无需签名‡

但是,它不会自动将任何类型变量引入到 local 绑定中。由于编译器确切地知道show_pair 的使用位置,因此它具有您需要拥有的至少一种类型实例化的完整上下文。它假设您需要一个实例化。这样,它会尝试为 monomorphic show_pair 推断某种类型,但失败了。只有通过添加显式签名,您才能强制编译器考虑多态签名。 正如 cmets 中所说,这实际上不是真的,因为自 GHC-7.0 以来,就有一个叫做 let generalisation 的东西即使没有签名,它也会使本地绑定多态。我不知道这是默认开启的。 — 即使 可能 可以省略签名,多态函数也应该更好地使用 IMO。


前提是您还没有在更高的范围内引入该变量,扩展名为-XScopedTypeVariables

‡不幸的是,monomorphism restriction 让这变得非常难以依赖。

【讨论】:

  • 为什么不让泛化抓住这个?
  • @dfeuer 我猜这是因为相互递归。对整个相互递归的定义组进行泛化,但为时已晚。
  • @chi,我猜这就是RelaxedPolyRec 或其他名称的意义所在?或者这也只是顶级?看来这里的标准有点太挑剔了。在任何情况下,我都在痴迷于类型签名写作阵营,并且害怕使这种痛苦的库。
  • 很确定这是因为多态递归,如果没有RelaxedPolyRec,情况会更糟:那么所有函数都需要签名。本地绑定的东西是MonoLocalBinds,如果你启用它,即使使用show_pair 上的签名,这段代码也不会工作。
  • 这个答案的最后一段具有误导性或错误:正如其他 cmets 已经提到的那样,在没有某些其他语言扩展的情况下,默认情况下让泛化处于启用状态,其确切目的是进行本地绑定多态的。
【解决方案2】:

好吧,我想您希望[0..] 被视为[Int],而事实并非如此。这可能是因为show_node for JObject[(String, JNode)] 作为与JArray 相同的参数将[(SOMETHING_UNKNOWN, JNode)] 传递给show_sect

尝试仅在表达式[0..] 上强制类型,您将与数组元素一起压缩:zip ([0..] :: [Int]) xs。我确信问题出在show_sect 的推断类型上。

我强烈建议提供一些类型注释,我认为这样做是一个好习惯。不过,我对 Haskell 中的类型推断不太熟悉。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多