gpt4 book ai didi

Haskell 推断出过于严格的类型

转载 作者:行者123 更新时间:2023-12-04 03:57:24 25 4
gpt4 key购买 nike

免责声明:我才刚刚开始学习 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

最佳答案

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

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

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

可以说 Haskell 也应该要求这样做,但事实并非如此。它只是量化您在显式签名中提到的任何变量†,因此您可以简单地编写
show_pair :: (Show a) => Int -> (a, JNode) -> String

此外,它尝试在没有签名的情况下将尽可能多的类型变量引入顶级绑定(bind)‡。

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

†假设您尚未在更高范围内引入该变量, -XScopedTypeVariables扩大。

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

关于Haskell 推断出过于严格的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32662237/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com