Haskell中的匿名函数类型

时间:2019-05-21 15:12:39

标签: haskell

在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

但是我完全担心键入匿名函数。了解这一点的魔力是什么?也许可以将此函数重写为“正常”函数以清楚地看到类型?

我的问题是:我们如何获得这些类型?他们来自哪里以及如何对此进行评估?

3 个答案:

答案 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 -> t2x的返回值没有固有的限制,因此最通用(允许)的类型是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

所以ax的类型,右边的整个表达式返回b

f x :: b

再次-在这里,我们在->的左侧和右侧都有一种表达式。

\f -> f x :: (a -> b) -> b