当我用Haskell编写*时发生了什么? [英] What is happening when I compose * with + in Haskell?

查看:87
本文介绍了当我用Haskell编写*时发生了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解

 (*)的结果。 (+)

在Haskell中。我知道组合运算符只是数学函数的标准组合,所以

 (f。g)= f(gx) 

但:

 (*)。 (+)::(Num(a→a),Num(a)=> a  - > (a  - > a) - > a  - > a 

我正在努力理解这种类型签名。我希望能够做到这样的事情:

 ((*)。(+))1 2 :: Num a => a  - > a 
=(*(+ 1 2))

)。 (+)的类型签名?我试着用类似的东西玩它(只是与其签名匹配):

 ((*)。(+)) 1(\ x  - > x + 1)1 

但是编译失败。我试图在撰写这些步骤时逐步了解逻辑步骤,但我并不完全了解它是如何得到这个结果的(以及结果是什么)。


解决方案

我明白你的感受。我最初发现功能组合也很难掌握。什么帮助我了解这件事是类型签名。考虑:
$ b

 (*):: Num x => x  - > x  - > x 
(+):: Num y => y - > y - > y
(。)::(b - > c) - > (a - > b) - > a - > c

现在当您编写(*)时。 (+)它实际上与(。)(*)(+)(即(*)是第一个参数(。)(+) code>(。)): $ b

  )::(b  - > c) - > (a  - > b) - > a  - > c 
| ______ | | ______ |
| |
(*)(+)

因此,类型签名 (*)(即 Num x => x - > x - > x )与 b - > c


$ b

 (*):: Num x => x  - > x  - > x  - 请记住`x  - > x  - > x` 
| | ____ | - 隐含地是`x - > (x - > x)`
| |
b - > c

(。)(*)::(a - > b) - > a - > c
| |
| | ~~~~ |
Num x => x x - > x

(。)(*):: Num x => (a - > x) - > a - > x - > x

因此,(+) Num y => y - > y - > y )与> Num x => a - > x


$ b

 (+):: Num y => y  - > y  - > y  - 请记住`y  - > y  - > y` 
| | ____ | - 隐含地是`y - > (y - > y)`
| |
Num x => a - > x

(。)(*)(+):: Num x => a - > x - > x
| | |
| | ~~~~ | | ~~~~ |
Num y => y y - > y y - > (*)(+)::(Num(y-> y),Num(y))=> y - > (y - > y) - > y - > y

我希望澄清 Num(y - > y) Num y 来自。你留下了一个非常奇怪的函数,类型为(Num(y - > y),Num y)=> y - > (y - > y) - > y - > y



这很奇怪的原因是它期望 y y - > y 成为 Num 的实例。可以理解的是, y 应该是 Num 的一个实例,但是 y - > ÿ?制作 y - > y Num 的实例似乎不合逻辑。这不可能是正确的。



但是,当您查看函数组合的实际情况时,它是有意义的:



< pre class =lang-hs prettyprint-override> (f。g)= \ z - > f(g z)

((*)。(+))= \ z - > (*)((+)z)

所以你有一个函数 \\ \\ z - > (*)((+)z)。因此 z 显然必须是 Num 的一个实例,因为(+)应用于它。因此, \z - > (*)((+)z) Num t => t - > ... 其中 ... (*)((+)z) $ b $ p $因此((+)z)的值为$ c $,这是我们稍后会发现的。

类型 Num t => t - > t 因为它需要一个数字。然而,在将其应用于另一个数字之前,将(*)应用于它。 因此(*)期望((+)z)是一个 Num ,这就是为什么 t - > t 预计会是 Num 的一个实例。因此, ... (t - > t) - > t - > t 和约束 Num(t - > t),导致类型(Num(t- > t),Num(t)=> t - > (t - > t) - > t - > t 。



您真正想将(*)(。:)

 (。:) ::(c  - > d) - > (a→b→c)→> a  - > b  - > d 
f。:g = \ x y - > f(gxy)

因此(*)。:(+) \xy - >相同。 (*)((+)x y)。现在有两个参数给(+)确保((+)xy)确实只是 Num t => t 而不是 Num t => t - > 。



因此((*)。:(+))2 3 5 (*)((+)2 3)5 ,它是(*)5 5 c $ c> 25 ,我相信是你想要的。



注意 f。:g 也可以写成(f。)。 g (。:)也可以定义为(。:) =(。)。 (。)。你可以在这里阅读更多关于它的信息:



什么是(f。)。 g的意思是在Haskell?



希望有帮助。


I'm trying to understand the result of

(*) . (+) 

in Haskell. I know that the composition operator is just the standard composition of mathematical functions- so

(f . g) = f (g x)

But:

(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a

I'm struggling to understand this type signature. I would have expected to be able to do things like:

((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))

What is the meaning of (*) . (+)'s type signature? I tried playing with it by something like (just matching up with its signature):

((*) . (+)) 1 (\x -> x + 1) 1

But that fails to compile. I'm trying to walk through the logical steps when composing these, but I'm not fully understanding how it's getting to this result (and what the result is).

解决方案

I understand how you feel. I found function composition to be quite difficult to grasp at first too. What helped me grok the matter were type signatures. Consider:

(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c

Now when you write (*) . (+) it is actually the same as (.) (*) (+) (i.e. (*) is the first argument to (.) and (+) is the second argument to (.)):

(.) :: (b -> c) -> (a -> b) -> a -> c
       |______|    |______|
           |           |
          (*)         (+)

Hence the type signature of (*) (i.e. Num x => x -> x -> x) unifies with b -> c:

(*) :: Num x => x -> x -> x -- remember that `x ->  x -> x`
                |    |____| -- is implicitly `x -> (x -> x)`
                |       |
                b ->    c

(.) (*) ::          (a -> b) -> a ->    c
                          |             |
                          |          |‾‾‾‾|
           Num x =>       x          x -> x

(.) (*) :: Num x => (a -> x) -> a -> x -> x

Hence the type signature of (+) (i.e. Num y => y -> y -> y) unifies with Num x => a -> x:

(+) :: Num y => y -> y -> y -- remember that `y ->  y -> y`
                |    |____| -- is implicitly `y -> (y -> y)`
                |       |
       Num x => a ->    x

(.) (*) (+) ::  Num x                => a ->     x    ->    x
                                        |        |          |
                                        |     |‾‾‾‾|     |‾‾‾‾|
                              Num y  => y     y -> y     y -> y

(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y

I hope that clarifies where the Num (y -> y) and Num y come from. You are left with a very weird function of the type (Num (y -> y), Num y) => y -> (y -> y) -> y -> y.

What makes it so weird is that it expects both y and y -> y to be instances of Num. It's understandable that y should be an instance of Num, but how y -> y? Making y -> y an instance of Num seems illogical. That can't be correct.

However, it makes sense when you look at what function composition actually does:

( f  .  g ) = \z ->  f  ( g  z)

((*) . (+)) = \z -> (*) ((+) z)

So you have a function \z -> (*) ((+) z). Hence z must clearly be an instance of Num because (+) is applied to it. Thus the type of \z -> (*) ((+) z) is Num t => t -> ... where ... is the type of (*) ((+) z), which we will find out in a moment.

Therefore ((+) z) is of the type Num t => t -> t because it requires one more number. However, before it is applied to another number, (*) is applied to it.

Hence (*) expects ((+) z) to be an instance of Num, which is why t -> t is expected to be an instance of Num. Thus the ... is replaced by (t -> t) -> t -> t and the constraint Num (t -> t) is added, resulting in the type (Num (t -> t), Num t) => t -> (t -> t) -> t -> t.

The way you really want to combine (*) and (+) is using (.:):

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)

Hence (*) .: (+) is the same as \x y -> (*) ((+) x y). Now two arguments are given to (+) ensuring that ((+) x y) is indeed just Num t => t and not Num t => t -> t.

Hence ((*) .: (+)) 2 3 5 is (*) ((+) 2 3) 5 which is (*) 5 5 which is 25, which I believe is what you want.

Note that f .: g can also be written as (f .) . g, and (.:) can also be defined as (.:) = (.) . (.). You can read more about it here:

What does (f .) . g mean in Haskell?

Hope that helps.

这篇关于当我用Haskell编写*时发生了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆