Haskell中匿名函数的类型
我在Haskell中键入匿名函数时遇到问题。例如,当我们有:Haskell中匿名函数的类型,haskell,Haskell,我在Haskell中键入匿名函数时遇到问题。例如,当我们有: \x -> x 5 在GHCI中检查的类型是numt1=>(t1->t2)->t2,而我确信它是相反的 \x -> a * x isnuma=>a->a(我知道我们需要假设a是一个整数,因为(*)的类型是Int->Int->Int(没有类型类)。 另一个例子是 \f -> f x 据我所知是(a->b)->b 但是我完全关心匿名函数的类型。理解这个函数的魔力是什么?也许可以将这个函数重写为“正常”函数以清楚地看
\x -> x 5
在GHCI中检查的类型是numt1=>(t1->t2)->t2
,而我确信它是相反的
\x -> a * x
isnuma=>a->a
(我知道我们需要假设a是一个整数,因为(*)的类型是Int->Int->Int(没有类型类)。另一个例子是
\f -> f x
据我所知是(a->b)->b
但是我完全关心匿名函数的类型。理解这个函数的魔力是什么?也许可以将这个函数重写为“正常”函数以清楚地看到类型
因此,我的问题是:我们如何获得这些类型?它们来自何处以及如何评估这些类型?以下是编写第一个函数的三种等效方法:
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::constraint1t1,constraint2t2=>t1->t2
。更具体地说,x
必须接受5
作为其第一个(可能是唯一的)参数。但这是其参数的唯一固有约束。5::Num t1=>t1
,因此x::Num t1,constraint t2=>t1->t2
。对于x
的返回值没有固有约束,因此最通用(允许的)类型是x::Num t1=>t1->t2
这给了我们函数参数的类型:\x->x5::Num t1=>(t1->t2)->?
。但是返回类型呢?好吧,函数只将x
应用于5,计算结果为(返回值)结果是,您的函数返回与x
相同的类型。假设您的函数可以接受任何可能的函数x
,则其类型为\x->x5::Num t1=>(t1->t2)->t2
还请注意,您可以对参数和类型变量使用任何小写名称。因此,您可以编写\function->function 5::Num five=>(five->result)->result
>如何获得这些类型
这些类型是Haskell系统可以从它唯一拥有的东西,即用户给出的定义(如“x5”)推断出来的
重要的不是函数是匿名的,而是函数不是显式类型的,因此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提到的类型推断。否则,如果类型约束相互矛盾或不明确,执行就会中止。我认为您不必以某种方式使它复杂化。基本上当您在Haskell中有一个匿名函数并希望查找类型您需要找到两种类型:首先是
->
符号之前的类型,然后是箭头右侧的整个表达式类型->
。根据您的示例:
\x -> x 5
我们需要找到x的类型,我们知道这是一个函数,它有一个属于typeclassNum
类型的参数,并且返回一些未知类型,比如说t
:
x :: Num a => a -> t
x 5 :: t
因此x
函数返回t
类型的内容:
x :: Num a => a -> 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
我不明白这里的问题是什么,除了一般的“我不明白类型推断是如何工作的”,太宽泛了。你能说得更具体些吗?@chi-an-issue基本上就是这些例子,它们对它们类型的评估,所以给出的结果将是computed@heisenberg7584您已经给出了类型。在理解为什么这些类型是正确的方面,您有什么具体问题?请编辑您的问题以添加此信息n、 可能重复:。如果不是重复,请说明原因。匿名函数在这里没有什么特别之处。
func x=x 5
与第一个函数完全相同,因此具有相同的类型。您还可以选择限制性更强的类型。例如,您可以定义更专业的函数版本as:\function->function 5::(浮点->字符串)->String
ok,这很有帮助…但是如果我们有一个表达式,比如;\x->fxy
。据我所知,这和func x=xy是一样的,对吧?当y甚至不是一个参数时,我们怎么能键入这个呢…@heisenberg7584正确,系统拒绝为\x->fxy指定一个类型,直到你定义了f和y。理论上,mayb它可以这样说(f::t1->t2->t3)=>t1->t3,但不能打扰。我想安全性比抱歉好。很抱歉,只是最后一个。所以当我们有\y->x+y时,类型是Num a=>a->a
,因为在表达式的左侧只有y,所以我们放了一些常量,例如x=3。所以我们从(+)中删除了一部分类型,因为x在这里是一个常量。如果我们的表达式是\xy->x+y,我们在这里没有常量,因此类型将是Num a=>a->a->a
,而另一方面g=x+y
的类型只是Num a=>a