如何在Haskell中打印空值

5
我正在尝试编写一个Haskell函数,它返回列表中的第一项。
h [] = Nothing
h (x:xs) = x

当我使用空列表调用它时:
main = print (h [])

我收到了如下错误消息:
prog.hs:4:8:
    No instance for (Show a0) arising from a use of `print'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Show Double -- Defined in `GHC.Float'
      instance Show Float -- Defined in `GHC.Float'
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus 23 others
    In the expression: print (h [])
    In an equation for `main': main = print (h [])

当我给函数一个空列表时,希望结果为


3
尝试为该函数加上类型签名,你就能在预期的位置得到错误提示。 - Niklas B.
1
请注意,您的函数目前是 [Maybe a] -> Maybe a,很可能是因为您在非空情况下忘记了 Just - JB.
2个回答

16

这里有几个问题,让我们先添加一个合理的类型标记

h :: [a] -> Maybe a
h [] = Nothing
h (x:xs) = x

现在我们得到了一个错误,我们正在返回一个普通的x,所以x需要是Maybe a类型。我们可能不想这样做,所以我们将其包装在Just构造函数中。
h (x:_) = Just x

现在我们来看一下你的问题。
请注意,这不仅适用于你的函数,而是适用于以下结果:
main = print $ head []
main = print $ id []
main = print $ tail []

所有的都是一样的。

[] 的类型是 [a]。由于我们没有指定 a 的类型,所以 h [] 的类型是 Show a => Maybe a。这里多了一个 Show 是因为我们想要打印输出结果。但实际上我们没有声明 a 的具体类型,导致 GHC 无法默认类型,从而出现错误。

有两种方法可以解决这个问题,其中一种笨拙的方法是将 h 变成单态类型(单态化?)h

h :: [Int] -> Maybe Int -- Bad

更聪明的方式是在我们的调用站点简单地选择一个具体类型。
main = print $ h ([] :: [Int])

我选择Int没有特别的原因,这并不是很重要。现在Maybe Int是可打印的,所以我们准备就绪了。 ::语法与顶级组件的语法相同,只需在表达式中声明[]的类型为[Int]即可。

有趣的事实是,GHCi比GHC具有更积极的默认设置。 这意味着

 main = print []

在GHCi中合法,但在GHC中不合法。如果你遇到了奇怪的行为,请询问表达式的类型以查看默认值。

 :t []

1
s / monomorphisize / [restrict](http://en.wikipedia.org/wiki/Restriction_ (mathematics))/ - 虽然我很喜欢你的说法。 - kqr
@kqr 哎呀,我还以为那就是真正的单词呢.. 唉,算了。 - daniel gratzer
GHCiه…پ许ن½؟用print []ه’Œprint Nothingم€‚è؟™هڈ¯èƒ½وک¯و··و·†çڑ„هژںه› م€‚ - Ingo
@jozefg,您能否展示最终代码?我尝试了一些您的代码,但它们仍在ideone.com上产生错误。 - wannik

1
编译器吃掉了你的函数,而且没有任何提示。
h [] = Nothing
h (x:xs) = x

但是h的类型是[Maybe a] -> Maybe a,而不是[a] -> Maybe a
h :: [a] -> Maybe a
h [] = Nothing
h (x:_) = Just x

[] 是多态类型 [a],因此函数结果 h [] 等于 Nothig 仍然是多态类型 Maybe a,但是 print 无法处理多态类型(如果我们没有实现 Show 的实例)。在 gchi 中可以运行。

> print (h [])

但是在这种情况下,gchi会将其转换为print ((h []) :: Maybe Int)

但是如果您有较少的多态函数,例如:

h [] = Nothing
h (x:_) = Just $ x == ""

然后编译器会找到类型为h :: [String] -> Maybe Bool的函数,并且print (h [])可以正常工作!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接