数字作为乘法函数(奇怪但有趣) [英] Numbers as multiplicative functions (weird but entertaining)
问题描述
在 Haskell中的默认函数组合的问题的评论 ,人们提到制作 Num
<
{ -
,所以我想用函数表示法来表示乘法: #LANGUAGE TypeFamilies# - }
import Control.Applicative
instance显示(a-> r)其中 - 在最近的GHC版本中不需要
show f =一个函数
实例等式(a-> r)其中 - 在最近的GHC版本中不需要
f == g =错误抱歉,哈斯克尔,我撒谎,我无法真正比较相等函数
实例(Num r,a〜r)=> Num(a - > r)其中
(+)= liftA2(+)
( - )= liftA2( - )
(*)= liftA2(*)
abs = liftA abs
negate = liftA否定
signum = liftA signum
fromInteger a =(fromInteger a *)
请注意,fromInteger定义意味着我可以编写计算结果为12的 3 4
,并且 7(2+ 8)
是70,就像你希望的那样。
* Main> 1 2 3
18
* Main> 1 2 4
32
* Main> 1 2 5
50
* Main> 2 2 3
36
* Main> 2 2 4
64
* Main> 2 2 5
100
* Main> (2 3)(5 2)
600
<编辑:使用Applicative而不是Monad因为Applicative通常是很好的,但它对代码没有太大的区别。]
2 3 4
包含您的实例,它们都是 2
和 3
是功能。因此 2
实际上是(2 *)
,并且类型为 Num a => a - >一个
。 3
是一样的。 2 3
然后是(2 *)(3 *)
,它与 2相同*(3 *)
。在你的实例中,这是 liftM2(*)2(3 *)
然后是 liftM2(*)(2 *)(3 *)
。现在这个表达式没有任何实例。
那么这是什么意思?那么, liftM2
对于函数来说是一种双重组合。特别是, liftM2 f g h
与 \ x - >相同。 f(g x)(h x)
。因此, liftM2(*)(2 *)(3 *)
就是 \ x - > (*)((2 *)x)((3 *)x)
。简化一下,我们得到: \ x - > (2 * x)*(3 * x)
。所以现在我们知道 2 3 4
实际上是(2 * 4)*(3 * 4)
p>
现在为什么函数以这种方式工作 liftM2
?让我们看看( - >)r
的monad实例(记住( - >)r
是(r - >)
,但我们不能编写类型级别的操作符部分):
instance Monad(( - >)r)其中
return x = \_ - > x
h>> = f = \ w - > f(hw)w
所以 return
是常量
。 >> =
有点奇怪。我认为用加入
来看这很容易。对于函数, join
的工作方式如下所示:
join f = \ x - > fxx
也就是说,它接受两个参数的函数并将其转换为一个参数的函数使用这个论点两次。够简单。这个定义也是有道理的。对于函数, join
必须将两个参数的函数变成一个函数;
>> =
是 fmap
,后跟加入
。对于函数, fmap
只是(。)
。所以现在>> =
等于:
h> ;> = f = join(f。h)
这就是:
h>> = f = \ x - > (f。h)xx
现在我们摆脱 code>得到:
h>> = f = \ x - > f(hx)x
现在我们知道>> =
的作品,我们可以看看 liftM2
。 liftM2
的定义如下:
liftM2 fab = a>> ; = \ a' - > b>> = \ b' - >返回(f a'b')
我们可以简单地点点滴滴。首先, return(f a'b')
变成 \ _ - > f a'b'
。结合 \ b' - >
,我们得到: \ b'_ - > f a'b'
。然后 b>> = \ b'_ - > f a'b'
变为:
\ x - > (\ b'_ - > f a'b')(bx)x
第二个 x
被忽略,我们得到: \ x - > (\ b' - > f a'b')(b x)
,然后将其减至 \ x - > f a'(b x)
。所以这给我们留下了:
a>> = \ a' - > \ x - > f a'(bx)
再次,我们用>> =
:
\ y - > (\ a'x - > f a'(bx))(ay)y
简化为:
\ y - > f(ay)(by)
这正是我们用作 liftM2
早些时候!
希望现在 2 3 4
的行为完全有意义。 / p>
In the comments of the question Tacit function composition in Haskell, people mentioned making a Num
instance for a -> r
, so I thought I'd play with using function notation to represent multiplication:
{-# LANGUAGE TypeFamilies #-}
import Control.Applicative
instance Show (a->r) where -- not needed in recent GHC versions
show f = " a function "
instance Eq (a->r) where -- not needed in recent GHC versions
f == g = error "sorry, Haskell, I lied, I can't really compare functions for equality"
instance (Num r,a~r) => Num (a -> r) where
(+) = liftA2 (+)
(-) = liftA2 (-)
(*) = liftA2 (*)
abs = liftA abs
negate = liftA negate
signum = liftA signum
fromInteger a = (fromInteger a *)
Note that the fromInteger definition means I can write 3 4
which evaluates to 12, and 7 (2+8)
is 70, just as you'd hope.
Then it all goes wonderfully, entertainingly weird! Please explain this wierdness if you can:
*Main> 1 2 3
18
*Main> 1 2 4
32
*Main> 1 2 5
50
*Main> 2 2 3
36
*Main> 2 2 4
64
*Main> 2 2 5
100
*Main> (2 3) (5 2)
600
[Edit: used Applicative instead of Monad because Applicative is great generally, but it doesn't make much difference at all to the code.]
In an expression like 2 3 4
with your instances, both 2
and 3
are functions. So 2
is actually (2 *)
and has a type Num a => a -> a
. 3
is the same. 2 3
is then (2 *) (3 *)
which is the same as 2 * (3 *)
. By your instance, this is liftM2 (*) 2 (3 *)
which is then liftM2 (*) (2 *) (3 *)
. Now this expression works without any of your instances.
So what does this mean? Well, liftM2
for functions is a sort of double composition. In particular, liftM2 f g h
is the same as \ x -> f (g x) (h x)
. So liftM2 (*) (2 *) (3 *)
is then \ x -> (*) ((2 *) x) ((3 *) x)
. Simplifying a bit, we get: \ x -> (2 * x) * (3 * x)
. So now we know that 2 3 4
is actually (2 * 4) * (3 * 4)
.
Now then, why does liftM2
for functions work this way? Let's look at the monad instance for (->) r
(keep in mind that (->) r
is (r ->)
but we can't write type-level operator sections):
instance Monad ((->) r) where
return x = \_ -> x
h >>= f = \w -> f (h w) w
So return
is const
. >>=
is a little weird. I think it's easier to see this in terms of join
. For functions, join
works like this:
join f = \ x -> f x x
That is, it takes a function of two arguments and turns it into a function of one argument by using that argument twice. Simple enough. This definition also makes sense. For functions, join
has to turn a function of two arguments into a function of one; the only reasonable way to do this is to use that one argument twice.
>>=
is fmap
followed by join
. For functions, fmap
is just (.)
. So now >>=
is equal to:
h >>= f = join (f . h)
which is just:
h >>= f = \ x -> (f . h) x x
now we just get rid of .
to get:
h >>= f = \ x -> f (h x) x
So now that we know how >>=
works, we can look at liftM2
. liftM2
is defined as follows:
liftM2 f a b = a >>= \ a' -> b >>= \ b' -> return (f a' b')
We can simply this bit by bit. First, return (f a' b')
turns into \ _ -> f a' b'
. Combined with the \ b' ->
, we get: \ b' _ -> f a' b'
. Then b >>= \ b' _ -> f a' b'
turns into:
\ x -> (\ b' _ -> f a' b') (b x) x
since the second x
is ignored, we get: \ x -> (\ b' -> f a' b') (b x)
which is then reduced to \ x -> f a' (b x)
. So this leaves us with:
a >>= \ a' -> \ x -> f a' (b x)
Again, we substitute >>=
:
\ y -> (\ a' x -> f a' (b x)) (a y) y
this reduces to:
\ y -> f (a y) (b y)
which is exactly what we used as liftM2
earlier!
Hopefully now the behavior of 2 3 4
makes sense completely.
这篇关于数字作为乘法函数(奇怪但有趣)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!