哈斯克尔怪异的表达 [英] Haskell weird expression

查看:137
本文介绍了哈斯克尔怪异的表达的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  Prelude>我想了解为什么以下是Haskell中的有效表达式:让e =(+)( - )
Prelude> :type e
e ::(Num(a - > a - > a),Num a)=> (a - > a - > a) - > a - > a - > a

更奇怪的是,表单中的任何表达式

  e 1 2 3 4 ... N 

无论N都是难以理解的类型的有效表达式。例如,

  Prelude> :te 1 2 3 4 5 
e 1 2 3 4 5
::(Num((a→a1→t)→a→a1→t) a→a→t),
Num(a→a1→t),Num a1,Num a)=>
t

这是咖啡和类型推断的不幸后果吗?



欢迎澄清。

解决方案

这不是一个不幸的后果。实际上,有些人可能会将其视为一项功能! (+)( - )的类型为

 > :t(+)
(+):: Num a => a - > a - > a
> :t( - )
( - ):: Num a => a - > a - > a

认识到 类型<$是有效的c $ c> a ,即使 a 是一个函数类型。因此,例如,如果类型 b - > b - > b 有一个 Num 实例,那么您可以将(+)限制为

 (+):: Num(b  - > b  - > b)=> (b  - > b  - > b) - > (b  - > b  - > b) - > b  - > b  - > b 

只需设定 a = b - > b - > B'/ code>。由于currying,最后三个 b s中的括号是不必要的(你可以写入它们,但它们会是多余的)。



现在, Num b => b - > b - > b 恰恰是( - )的类型(前提条件是 b 本身具有有一个 Num 实例),所以函数( - )填充<$ c $的第一个槽 c>(+)(+)( - )的类型是

<$ (+)( - )::(Num b,Num(b→b→b))→> (b - > b - > b) - > b - > b - > b

这就是您观察到的情况。






这就提出了为什么实际上对函数有一个 Num 实例可能是有用的问题。事实上,为函数定义一个 Num 实例是否有意义?我声称它的确如此!你可以定义

  instance Num a => Num(r→a)其中,
(f + g)r = fr + gr
(f-g)r = fr -gr
(f * g)r = fr * gr
abs fr = abs(fr)
signum fr = signum(fr)
fromInteger nr = fromInteger n

这非常适合作为 Num 实例。事实上,这正是您需要解释表达式的实例 e -

 >让e =(+)( - )
> e 3 2 1
4



Buh?!?



发生了以下事情。由于(Num a)=> r - >一个是任何 r 的有效 Num 实例,您可以将 r with a - >一个,它表明(Num a)=> a - > a - >一个也是一个有效的 Num 实例。所以你有

   - 请记住(+)f = \g r  - > fr + gr 

(+)( - )3 2 1
=(\ grs - >( - )rs + grs)3 2 1 - 函数
=(\ rs - >( - )rs + 3 rs)2 1 - beta缩减
=(\ s - >( - )2 s + 3 2 s)1 - β减少
=( - )2 1 + 3 2 1 - β减少
=(2-1)+ 3 - 因为(3 2)= 3和(3 1)= 3有一点令人费解(特别是,有些人认为,请确保你理解为什么 3 2 = 3 ),但是一旦你扩展了所有的定义,不要太混淆!



< hr>

您要求派生Haskell使用的(+)( - )类型。它依赖于类型变量的统一。它就像这样 -


  1. 您知道(+):: Num a => a - > a - > a ( - ):: Num b => b - > b - > b (我使用不同的字母,因为我们希望将它们混合在一起)

  2. 如果要将( - )放入(+)的第一个槽中,您必须具有 a〜b - > b - > b ,所以组合的类型是
  3. (+)( - )::(Num a,Num b,a〜b - > b - > b)=> (b - > b - > b) - > (b - > b - > b)

  4. 现在,您将 a code> b - > b - > b (如胖箭头左边所示,由标志),让您留下 < (+)( - )::(Num(b→b→b),Num b)=> (b - > b - > b) - > (b - > b - > b)

  5. 如果我们删除最右括号(因为它们是多余的)并将 b a ,这是Haskell推断的类型签名。


I would like to understand why the following is a valid expression in Haskell:

Prelude> let e = (+) (-)
Prelude> :type e
e :: (Num (a -> a -> a), Num a) => (a -> a -> a) -> a -> a -> a

More strange is that any expression in the form

e 1 2 3 4 ... N

whatever N are all valid expressions of uncomprehensible type. For example,

Prelude> :t e 1 2 3 4 5
e 1 2 3 4 5
  :: (Num ((a -> a1 -> t) -> (a -> a1 -> t) -> a -> a1 -> t),
      Num (a -> a1 -> t), Num a1, Num a) =>
     t

Is this an unfortunate consequence of currying and type inference?

Clarifications are welcome.

解决方案

It's not an "unfortunate consequence". In fact, some might see it as a feature! The types of (+) and (-) are

> :t (+)
(+) :: Num a => a -> a -> a
> :t (-)
(-) :: Num a => a -> a -> a

It's important to realize that this is valid for any type a, even if a is a function type. So, for example, if the type b -> b -> b has a Num instance, then you could restrict (+) to

(+) :: Num (b -> b -> b) => (b -> b -> b) -> (b -> b -> b) -> b -> b -> b

by simply setting a = b -> b -> b. Because of currying, the parentheses around the last three bs are unnecessary (you could write them in but they would be redundant).

Now, Num b => b -> b -> b is exactly the type of (-) (with the proviso that b itself has to have a Num instance), so the function (-) fills the first "slot" of (+) and the type of (+) (-) is

(+) (-) :: (Num b, Num (b -> b -> b)) -> (b -> b -> b) -> b -> b -> b

which is what you observed.


This raises the question of why on earth it might be useful to have a Num instance for functions at all. In fact, does it even make sense to define a Num instance for functions?

I claim that it does! You can define

instance Num a => Num (r -> a) where
    (f + g) r = f r + g r
    (f - g) r = f r - g r
    (f * g) r = f r * g r
    abs f r = abs (f r)
    signum f r  = signum (f r) 
    fromInteger n r = fromInteger n

which makes perfect sense as a Num instance. And in fact, this is precisely the instance you need to interpret your expression e -

> let e = (+) (-)
> e 3 2 1
4

Buh?!?

What happened is the following. Since (Num a) => r -> a is a valid Num instance for any r, you can replace r with a -> a, which shows that (Num a) => a -> a -> a is also a valid Num instance. So you have

-- Remember that (+) f = \g r -> f r + g r

  (+) (-) 3 2 1
= (\g r s -> (-) r s + g r s) 3 2 1 -- definition of (+) on functions
= (\  r s -> (-) r s + 3 r s) 2 1   -- beta reduction
= (\    s -> (-) 2 s + 3 2 s) 1     -- beta reduction
=            (-) 2 1 + 3 2 1        -- beta reduction
=            (2 - 1) + 3            -- since (3 2) = 3 and (3 1) = 3
=               1    + 3
=               4

A little convoluted (in particular, make sure you understand why 3 2 = 3) but not too confusing once you've expanded all the definitions out!


You asked for the derivation of the type of (+) (-) that Haskell uses. It relies on the idea of "unification" of type variables. It goes something like this -

  1. You know that (+) :: Num a => a -> a -> a and (-) :: Num b => b -> b -> b (I use different letters because we will want to mash these together).
  2. If you are going to put (-) into the first slot of (+) you must have a ~ b -> b -> b, so the combined type is
  3. (+) (-) :: (Num a, Num b, a ~ b -> b -> b) => (b -> b -> b) -> (b -> b -> b)
  4. Now you "unify" a with b -> b -> b (as indicated on the left of the fat arrow, by the ~ sign) which leaves you with
  5. (+) (-) :: (Num (b -> b -> b), Num b) => (b -> b -> b) -> (b -> b -> b)
  6. If we remove the right-most parentheses (because they are redundant) and rename b to a, this is the type signature that Haskell infers.

这篇关于哈斯克尔怪异的表达的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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