免责声明:我刚刚开始学习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
答案 0 :(得分:9)
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 绑定。由于编译器确切地知道 正如评论中所述,这实际上并不正确,因为自GHC-7.0以来,有一个叫做{{3即使没有签名,它也会使本地绑定变为多态。我不知道这是默认开启的。 - 即使可能省略签名,多态函数仍然应该更好地使它们成为IMO。 show_pair
的确切使用位置,因此它具有至少一个您需要具有的类型实例化的完整上下文。它假设您只需要一个实例。有了它,它试图推断单态 show_pair
的某种类型,并失败。只有通过添加显式签名才能强制编译器考虑多态签名。
† 如果您尚未在更高的范围内引入该变量,并使用-XScopedTypeVariables
扩展名。
‡ 不幸的是,let generalisation使得这非常难以依赖。
答案 1 :(得分:0)
好吧,我认为您希望[0..]
被视为[Int]
,而事实似乎并非如此。这可能是因为show_node
JObject
传递[(String, JNode)]
,因为与JArray
相同的参数会将[(SOMETHING_UNKNOWN, JNode)]
传递给show_sect
。
尝试强制使用与数组元素一起压缩的表达式[0..]
上的类型:zip ([0..] :: [Int]) xs
。我确信问题是推断的show_sect
类型。
我强烈建议提供一些类型的注释,我认为这是一个很好的做法。我对Haskell中的类型推断并不太熟悉。