我刚开始学习Haskell并且正在阅读“Learnyouahaskell”一书 . 我遇到过这个例子
tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
我知道 (Show a)
这里是一个类约束和参数类型,在这种情况下 a
必须能够"showable" .
考虑到 a
这里是列表而不是列表的元素,为什么我不能声明这样的函数: -
tell :: (Show a) =>a->String
编辑1: - 从下面的答案我似乎明白,需要为模式匹配指定 a
的具体类型 . 考虑到这一点,下面的正确实施是什么: -
pm :: (Show a) =>a->String
pm 'g'="wow"
它给我的错误如下
Could not deduce (a ~ Char)
from the context (Show a)
bound by the type signature for pm :: Show a => a -> String
at facto.hs:31:7-26
`a' is a rigid type variable bound by
the type signature for pm :: Show a => a -> String at facto.hs:31:7
In the pattern: 'g'
In an equation for `pm': pm 'g' = "wow"
失败,模块加载:无 .
我从错误消息中了解到它无法推断 a
的具体类型,但是如何使用 Show
来声明它 .
我知道我可以这样解决上述问题: -
pmn :: Char->String
pmn 'g'="wow"
但我只是想正确理解 Show
类型类
5 回答
List确实实现
Show
类型类,但当你说:Show a => a -> String
这意味着该函数将接受任何实现Show
的类型 . 最重要的是,你只能调用show class函数,你的函数永远不会知道a
的具体类型 . 而你试图在a
上调用列表模式匹配Update for new edit in question:
正确的实现将是:
pm c ="wow"
. 您可以在参数c
上调用任何Show
类型的类函数 . 您不能像以前那样进行模式匹配,因为您不知道参数的确切类型,您只知道它实现了Show
类型类 . 但是当您将Char
特定为类型时,模式匹配才起作用在这两个签名中,
a
isn 't a list -- it'是任何类型,你不能选择哪个(除了它必须是Show
的实例) .在
您在
a
的列表上匹配,而不是在a
类型的值上匹配 .如果你写的
你会假设类型
a
是列表的类型(东西) . 但它可以是任何类型,例如Bool
.(但是我可能不理解你的问题 - 你还没有真正说出问题所在 . 当问这样的问题时,你应该通常指明你做了什么,你期望什么,发生了什么 . 这些都不是这里确实指明了,所以人们只能猜测你的意思 . )
问题不在于
Show
. 确实,如果我们尝试:我们得到了类型检查错误 . 让我们看看它的内容:
现在我们问自己,这个所谓的'type'构造真的意味着什么?当你写
tell2 :: a -> String
时,你所说的是对于任何类型的确切的a,tell2
会给我们一个String
.[a]
(或[c]
或[foo]
- 名称无关紧要)并不完全是a
. 这似乎是一种随意的区分,据我所知,它是 . 让我们看看写作时会发生什么如你所知,写
t
和a
之间没有区别,[Char]
只是String
的类型同义词,所以我们写的类型和GHC类型的推断是 identical .嗯,不太好 . 当您自己,程序员在源中手动指定函数的类型时,类型签名中的类型变量将变得僵化 . 这究竟是什么意思?
来自https://research.microsoft.com/en-us/um/people/simonpj/papers/gadt/:
所以,只是因为你写出来的事实,类型签名变得不同了 . 在这种新型语法中,我们有
a /= [b]
. 对于刚性类型签名,GHC将推断它可以提供的信息量最少 . 它必须从模式绑定推断a ~ [b]
;但是它不能从您提供的类型签名中进行推断 .让我们看看GHC为原始函数提供的错误:
我们再次看到
rigid type variable
等,但在这种情况下,GHC还声称它无法推断出某些东西 . (顺便说一下 - 类型语法中的a ~ b === a == b
) . 类型检查器实际上是在类型中查找使函数有效的约束;它没有找到它并且足够好以告诉你它需要使它有效:如果我们插入GHC 's suggestion verbatim, it type checks, since now GHC doesn' t需要进行任何推断;我们已经准确地告诉了
a
是什么 .一旦你在'g'上模式匹配,例如
你的函数不再具有
(Show a) => a -> String
的类型;相反,它具有'a'的具体类型,即Char,因此它变为Char -> String
这与您给出的显式类型签名直接冲突,表明你的函数适用于任何类型'a'(只要该类型是
Show
的实例) .在这种情况下,您无法模式匹配,因为您在Int,Char等上进行模式匹配 . 但您可以在Prelude中使用
show
函数:你可能已经猜到了,
show
有点神奇;) . 实际上,为每个类型实现了一大堆show
函数,它们是Show
类型类的实例 .这表示
tell
接受可显示的任何类型a
的值 . 你可以在任何可以看到的东西上调用它 . 这意味着在tell
的实现中,你必须能够对任何事物进行操作(这是可以显示的) .您可能认为这对于该类型签名来说是一个好的实现:
因为列表确实可以显示,所以第一个参数的有效值也是如此 . 但是如果我打电话给
tell 1
或tell True
或tell (1, 'c')
等,我就有意义了 .在
tell
内,a
类型可以是Show
实例的任何类型 . 所以我能用这个值做的唯一事情就是对所有类型_1144034_有效的事情 . 这基本上意味着您只能将其传递给具有通用Show a => a
参数的其他类似函数您的混淆源于对类型签名
tell :: (Show a) => [a] -> String
的误解"Considering that a here is a list and not an element of the list" . 这里a
实际上是列表的一个元素,而不是列表本身 .该类型签名读取"tell takes a single paramter, which is a list of some showable type, and returns a string" . 这个版本的
tell
知道它接收一个列表,所以它可以用它的参数做一些列表 . 列表中的内容是某些未知类型的成员 .1除了将值传递给另一个
Show
函数之外,大多数这些函数也无法对值进行任何操作,但迟早会忽略该值或将其传递给Show
类型类中的一个实际函数;它们具有针对每种类型的专用实现,因此每个专用版本都可以知道它正在运行的类型,这是最终可以完成的唯一方式 .