2012-08-26 75 views
17

Tacit function composition in Haskell,人們提到製作Num實例a -> r問題的意見,所以我想我會用函數符號來表示乘法玩:號作爲乘法功能(奇怪,但娛樂)

{-# 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 *) 

請注意,FromInteger定義意味着我可以編寫3 4,其值爲12,而7 (2+8)爲70,就像您希望的那樣。

然後這一切都變得奇妙,有趣怪異!請解釋這個wierdness如果可以的話:

*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 

[編輯:使用應用型的,而不是由於Monad的是應用型通常很大,但它並沒有太大的差別在所有代碼]

+2

在GHC 7.4中,可以刪除虛擬'Show'和'Eq'實例,因爲'Num'不再需要它們。 – sdcvvc

+3

'Monad'在這裏矯枉過正。更簡單和更通用的「應用程序」就足夠了。 – Conal

+0

@sdcvvc我很快就會升級,是的。 – AndrewC

回答

21

在表達式如2 3 4與您的實例,23都是函數。所以2實際上是(2 *)並且有一個類型Num a => a -> a3是一樣的。 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)

那麼,爲什麼liftM2函數的工作方式是這樣呢?讓我們來看看單子實例(->) r(記住,(->) r(r ->)但我們不能寫的類型級運營商的部分):

instance Monad ((->) r) where 
    return x = \_ -> x 
    h >>= f = \w -> f (h w) w 

所以returnconst>>=有點奇怪。我認爲在join中看到這個更容易。對於函數,join是這樣的:

join f = \ x -> f x x 

也就是說,它需要兩個參數的函數,並使用該參數兩次把它變成一個參數的函數。夠簡單。這個定義也是有道理的。對於函數,join必須將兩個參數的函數變成一個函數;要做到這一點的唯一合理的方法是兩次使用這一個參數。

>>=fmap其次是join。對於功能,fmap只是(.)。所以,現在>>=等於:

h >>= f = join (f . h) 

這僅僅是:

h >>= f = \ x -> (f . h) x x 

現在我們剛剛擺脫的.獲得:

h >>= f = \ x -> f (h x) x 

所以,現在我們知道了如何>>=作品,我們可以看看liftM2liftM2定義如下:

liftM2 f a b = a >>= \ a' -> b >>= \ b' -> return (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') (b x) x 

因爲第二x被忽略,我們得到:\ x -> (\ b' -> f a' b') (b x)然後將其還原到\ x -> f a' (b x)。因此,這給我們留下了:

a >>= \ a' -> \ x -> f a' (b x) 

我們再次替換>>=

\ y -> (\ a' x -> f a' (b x)) (a y) y 

這簡化爲:

\ y -> f (a y) (b y) 

這正是我們作爲liftM2前面!

希望現在2 3 4的行爲完全合理。

+0

啊是的 - 只要你有'liftM2(*)(2 *)(3 *)4'我看到了爲什麼它是最後一個參數的平方 - 這只是意味着'(+)$(2 *)4 $( 3 *)4'。和'(2 3)(5 2)'有不必要的括號,所以它只是'2 4(5 2)',出於同樣的原因是300。 – AndrewC

+0

順便說一句,我對'( - >)r'的Applicative和Monad實例感到滿意,並且應該在問題中這樣說,這只是我的大腦剛從我的耳朵裏泄漏出來,當我做了'2 3 4 ',而我甚至沒有嘗試手工評估。衛生署!你的解釋會讓其他人更加清楚,所以也要謝謝。 – AndrewC

+2

@AndrewC:我剛剛寫下自己的思維過程,當時我正在搞清楚「2 3 4」究竟做了什麼,所以它和其他任何人一樣幫助自己:P。 –