在Haskell中键入匿名函数时遇到问题。例如,当我们有:
\x -> x 5
在GHCI中检查的类型为Num t1 => (t1 -> t2) -> t2
,但我确定是相反的。
\x -> a * x
是Num a => a -> a
(我知道我们需要假设a是一个整数,因为(*)的类型是Int-> Int-> Int(没有类型类)。
另一个例子是
\f -> f x
据我所知,某事就像(a-> b)-> b
但是我完全担心键入匿名函数。了解这一点的魔力是什么?也许可以将此函数重写为“正常”函数以清楚地看到类型?
我的问题是:我们如何获得这些类型?他们来自哪里以及如何对此进行评估?
答案 0 :(得分:5)
>>我们如何获得这些类型?
这些类型是Haskell系统可以从其唯一的东西(即用户给定的定义,例如“ x 5”)中得出的。
重要的不是函数是匿名的。这是因为该函数未显式键入,因此Haskell系统必须从表达式中“猜测”该类型。
Prelude> let a=4
Prelude> :t \x -> a * x
\x -> a * x :: Num a => a -> a
Prelude>
Prelude> let f1 x = a*x
Prelude> :t f1
f1 :: Num a => a -> a
Prelude>
因此,匿名和命名版本的类型完全相同。
当然,您可以更具体:
Prelude> let f4 :: Double -> Double ; f4 x = 4*x
Prelude> :t f4
f4 :: Double -> Double
Prelude>
Haskell不会强迫您显式键入所有内容。 它将所有显式键入信息(例如对于f4)以及定义和调用库函数所产生的隐式键入信息作为键入约束。
如果可以明确解决约束,则可以;如上文 chi 所述,这称为类型推断。否则,如果键入约束是矛盾的或模棱两可的,则执行将中止。
答案 1 :(得分:2)
以下是三种等效的编写第一个函数的方法:
f :: Num t1 => (t1 -> t2) -> t2
f = \x -> x 5
g :: Num t1 => (t1 -> t2) -> t2
g x = x 5
h :: Num t1 => (t1 -> t2) -> t2
h = ($ 5)
请注意,无论您在定义中使用哪种语法,此函数都是二阶函数。这意味着其参数x
本身就是一个函数。也就是说,x
的类型必须为x :: Constraint1 t1, Constraint2 t2 => t1 -> t2
。更具体地说,x
必须接受5
作为其第一个(可能是唯一的)参数。但这是对其论点的唯一固有限制。 5 :: Num t1 => t1
,所以x :: Num t1, Constraint2 t2 => t1 -> t2
。 x
的返回值没有固有的限制,因此最通用(允许)的类型是x :: Num t1 => t1 -> t2
。
这样就为我们提供了函数参数的类型:\x -> x 5 :: Num t1 => (t1 -> t2) -> ?
。但是返回类型呢?好吧,您的函数仅将x
应用于5并求值(返回)结果,因此您的函数返回与x
相同的类型。假设您的函数可以接受任何可能的函数x
,则其类型为\x -> x 5 :: Num t1 => (t1 -> t2) -> t2
。
还请注意,您可以为参数和类型变量使用所需的任何小写名称。因此,您也可以编写\function -> function 5 :: Num five => (five -> result) -> result
。
答案 2 :(得分:1)
我认为您正在以不必要的方式使其复杂化。基本上,当您在Haskell中具有匿名函数并想要查找其类型时,您需要查找两种类型:首先是->
符号之前的一种,然后是表达式右侧的整个表达式的类型。箭头->
。根据您的示例:
\x -> x 5
我们需要找到x的类型,我们知道它是一个函数,该函数具有一个属于类型类Num
的某个类型的参数,并返回某些未知类型-假设t
:>
x :: Num a => a -> t
因此,x
函数返回的类型为t
:
x 5 :: t
这是我们的答案:
\x -> x 5 :: Num a => (a -> t) -> t
最后一种情况相同:
\f -> f x
f又是一个函数,但是这次我们不知道其参数的类型
f :: a -> b
所以a
是x
的类型,右边的整个表达式返回b
f x :: b
再次-在这里,我们在->
的左侧和右侧都有一种表达式。
\f -> f x :: (a -> b) -> b