哈斯克尔怪异的表达 [英] Haskell weird expression
问题描述
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使用的
(+)( - )
类型。它依赖于类型变量的统一。它就像这样 -
- 您知道
(+):: Num a => a - > a - > a
和( - ):: Num b => b - > b - > b
(我使用不同的字母,因为我们希望将它们混合在一起)
- 如果要将
( - )
放入(+)
的第一个槽中,您必须具有a〜b - > b - > b
,所以组合的类型是(+)( - )::(Num a,Num b,a〜b - > b - > b)=> (b - > b - > b) - > (b - > b - > b)
- 现在,您将
a
code> b - > b - > b〜
标志),让您留下 < (+)( - )::(Num(b→b→b),Num b)=> (b - > b - > b) - > (b - > b - > b)
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 b
s 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 -
- 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). - If you are going to put
(-)
into the first slot of(+)
you must havea ~ b -> b -> b
, so the combined type is (+) (-) :: (Num a, Num b, a ~ b -> b -> b) => (b -> b -> b) -> (b -> b -> b)
- Now you "unify"
a
withb -> b -> b
(as indicated on the left of the fat arrow, by the~
sign) which leaves you with (+) (-) :: (Num (b -> b -> b), Num b) => (b -> b -> b) -> (b -> b -> b)
- If we remove the right-most parentheses (because they are redundant) and rename
b
toa
, this is the type signature that Haskell infers.
这篇关于哈斯克尔怪异的表达的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!