3

在 Haskell 中,为什么会这样编译:

splice :: String -> String -> String
splice a b = a ++ b
main = print (splice "hi" "ya")

但这不会:

splice :: (String a) => a -> a -> a
splice a b = a ++ b
main = print (splice "hi" "ya")

>> Type constructor `String' used as a class

我会认为这些是同一件事。有没有办法使用第二种样式,避免重复类型名称 3 次?

4

5 回答 5

13

types 中的=>语法适用于typeclasses

当您说 时f :: (Something a) => a,您并不是说那a a Something,而是说它是“类型组中的”Something类型。

例如,Num是一个类型类,其中包括 和 等Int类型Float。不过,没有类型Num,所以我不能说

f :: Num -> Num
f x = x + 5

但是,我可以说

f :: Int -> Int
f x = x + 5

或者

f :: (Num a) => a -> a
f x = x + 5
于 2012-05-07T19:10:28.803 回答
3

其实是可以的:

Prelude> :set -XTypeFamilies
Prelude> let splice :: (a~String) => a->a->a; splice a b = a++b
Prelude> :t splice
splice :: String -> String -> String

这使用了等式约束~。但我会避免这种情况,它并不比简单地编写短得多String -> String -> String,而是更难理解,编译器也更难解决。

于 2012-05-07T19:28:47.737 回答
3

有没有办法使用第二种样式,避免重复类型名称 3 次?

为了简化类型签名,您可以使用类型同义词。例如你可以写

type S = String
splice :: S -> S -> S

或类似的东西

type BinOp a = a -> a -> a
splice :: BinOp String

但是,对于像 一样简单的东西String -> String -> String,我建议只输入它。类型同义词应该用于使类型签名更具可读性,而不是更少。

在这种特殊情况下,您还可以将类型签名概括为

splice :: [a] -> [a] -> [a]

因为它根本不依赖于作为字符的元素。

于 2012-05-07T20:29:58.027 回答
1

嗯...String是一种类型,而您正试图将其用作一个类。

如果您想要splice函数的多态版本示例,请尝试:

import Data.Monoid

splice :: Monoid a=> a -> a -> a
splice = mappend

编辑:所以这里的语法是出现在左边的大写单词=>类型类约束出现在右边的变量=>。右边的所有大写单词都是类型的名称

于 2012-05-07T18:17:44.827 回答
1

您可能会在Learn You a Haskell章节中找到方便的解释。

于 2012-05-07T19:10:47.483 回答