->
和=>
两种符号。例如:我可以自己定义类型声明。addMe :: Int -> Int -> Int
addMe x y = x + y
它能够正常工作。
但是如果我们查看:t sqrt
,我们会得到:
sqrt :: Floating a => a -> a
我们什么时候使用=>
,什么时候使用->
?
在什么情况下使用"fat arrow",在什么情况下使用"thin arrow"?
->
和=>
两种符号。例如:我可以自己定义类型声明。addMe :: Int -> Int -> Int
addMe x y = x + y
它能够正常工作。
但是如果我们查看:t sqrt
,我们会得到:
sqrt :: Floating a => a -> a
我们什么时候使用=>
,什么时候使用->
?
在什么情况下使用"fat arrow",在什么情况下使用"thin arrow"?
->
用于显式函数。即当f
可以写成f x
形式的表达式时,签名必须有其中一个箭头†。具体来说,x
(参数)的类型必须出现在->
箭头的左侧。=>
视为函数箭头‡。它在逻辑意义上是一个蕴含箭头:如果a
是一个具有Floating a
属性的类型,则可以得出sqrt
的签名是a -> a
。addMe
示例,它是一个具有两个参数的函数,其签名必须始终采用x -> y -> z
的形式。可能还可以在前面加上q =>
;这不影响函数性质,但可能会对允许使用的特定类型产生影响。通常,如果类型已经固定和具体,则不需要这样的约束。例如,您可以原则上对Int
施加约束:addMe :: Num Int => Int -> Int -> Int
addMe x y = x + y
...但是这并没有实际带来任何成效,因为每个人都知道特定类型的 Int
是数字类 Num
的一个实例。你需要这样的约束条件的情况是当类型不是固定的而是一个类型变量(小写字母),也就是说函数是多态的时候。你不能只写
addMe' :: a -> a -> a
addMe' x y = x + y
因为那个签名会暗示该函数适用于任何类型a
,但它不能适用于所有类型(例如,你如何添加两个字符串?好吧,也许不是最好的例子,但你如何乘两个字符串?)addMe' :: Num a => a -> a -> a
addMe' x y = x + y
这意味着,您不关心确切的类型a
是什么,但需要它是一个数值类型。任何人都可以使用自己的类型MyNumType
调用该函数,但他们需要确保满足Num MyNumType
:然后就会得出 addMe'
的签名为MyNumType -> MyNumType -> MyNumType
。
要实现这一点,可以使用您知道是数值类型的标准类型,例如addMe' 5.9 3.7 :: Double
将起作用,或者为您的自定义类型和Num
类提供一个实例声明。只有当您确定这是一个好主意时才执行后者;通常,标准的num类型就足够了。
†请注意,签名中可能看不到箭头:type IntEndofunc = Int -> Int
时,当f :: IntEndofunc; f x = x+x
时是可以的。但是,您可以认为typedef本质上只是语法包装器;它仍然是相同的类型,其中确实包含箭头。
‡恰好这样,逻辑蕴含和函数应用可以看作是同一数学概念的两个方面。此外,GHC实际上将类约束实现为函数参数,称为字典。但所有这些都是在幕后进行的,因此如果有什么东西,它们就是隐式函数。在标准Haskell中,您永远不会看到=>
类型的LHS作为某个实际参数的类型来应用该函数。
RankNTypes
,匹配并立即重新应用newtype
构造函数可能不是无操作。我记不清细节了,但它与类型参数的更改有关,导致在newtype
下类约束的类型发生变化,因此包装值必须使用传递的字典进行实际更改。如果您感兴趣,我可以找出来;我相信它是在被拒绝的primitive
PR中,并且与遍历数组有关。 - dfeuer"细箭头"用于函数类型(t1 -> t2
是一个函数类型,它接受类型为t1
的值并生成类型为t2
的值)。
"粗箭头"用于类型约束。它将多态函数的类型约束列表与其余类型分开。因此,给定 Floating a => a -> a
,我们有函数类型 a -> a
,即可以使用任何类型 a
的参数并生成相同类型的结果的函数类型,添加了约束条件 Floating a
,这意味着该函数实际上只能与实现了 Floating
类型类的类型一起使用。
show :: Show a => a -> String
可以将任意类型 a
的值转换为字符串,但仅当类型 a
实现了 Show
接口时才能执行转换。 - melpomeneFloat
类型或者任何实现了 Floating
接口的类型吗? - sepp2kFloat
,类型应该是addMe :: Float -> Float -> Float
。如果要使用任何实现了Floating
的类型,那么它将是addMe :: Floating a => a -> a -> a
。 - sepp2k->
是函数的构造器,=>
用于约束,类似于 Haskell 中称为类型类的“接口”。
以下是一个小例子:
sum :: Int -> Int -> Int
sum x y = x + y
该函数仅允许Int类型,但如果您想要一个巨大的int或小型的int,您可能需要使用Integer,并且如何告诉它同时使用两者?
sum2 :: Integral a => a -> a -> a
sum2 x y = x + y
现在如果你尝试这样做:
sum2 3 1.5
它会给你一个错误
另外,你可能想知道两个数据是否相等,你需要:
equals :: Eq a => a -> a -> Bool
equals x y = x == y
现在如果你这样做:
3 == 4
没问题
但如果你创建:
data T = A | B
equals A B
它将会给你:
error:
• No instance for (Eq T) arising from a use of ‘equals’
• In the expression: equals A B
In an equation for ‘it’: it = equals A B
data T = A | B deriving Eq
equals A B
False
=>
是用于表示类型约束的符号,箭头(->)
则是函数类型构造器。 - Willem Van Onsem