2011-06-09 49 views
11
import Control.Applicative 
import Control.Arrow 

filter ((&&) <$> (>2) <*> (<7)) [1..10] 
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10] 

兩者都得到相同的結果!但是,我很難理解。這裏有人能詳細解釋嗎?你介意在論壇中解釋代碼嗎?

+0

這些例子來自哪裏? – 2011-06-09 03:33:57

+0

應用程序確實需要自己的語法糖,適用於普通應用程序和中綴應用程序。 '過濾器((> 2)<(&&)>(<7))[1..10]'會很好地讀取。 – 2011-06-09 03:44:44

+0

@pelotom:總會有成語括號。我們現在有monad理解,爲什麼不能應用理解? :) – 2011-06-09 03:58:24

回答

23

讓我們從第二個開始,這更簡單。我們有兩個神祕的運營商在這裏,有以下幾種類型:

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c') 
(>>>) :: Category cat => cat a b -> cat b c -> cat a c 

ArrowCategory型類主要是對的事情,表現得像功能,其中當然也包括函數本身,而且這裏兩個實例都只是普通的(->)。所以,重寫類型以使用:

(&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c')) 
(>>>) :: (a -> b) -> (b -> c) -> (a -> c) 

第二具有非常相似類型(.),熟悉的功能組合物算子;實際上,它們是相同的,只是換了參數。第一種是更不熟悉的,但是這些類型再次告訴你所有你需要知道的東西 - 它有兩個函數,它們接受一個通用類型的參數,並且產生一個單獨的函數,將這兩個函數結合成一個元組。

因此,表達式(>2) &&& (<7)採用單個號碼,併產生一個對基於所述比較Bool值。然後將這個結果輸入到uncurry (&&),它只需要一對Bool s和與它們在一起。得到的謂詞用於以通常的方式過濾列表。


第一個比較神祕。我們有兩個神祕的運營商,再次,有以下類型:

(<$>) :: Functor f => (a -> b) -> f a -> f b 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 

觀察到的(<$>)在這種情況下,第二個參數是(>2),其類型爲(Ord a, Num a) => a -> Bool,而(<$>)的參數的類型有類型f a。這些如何兼容?

答案是,正如我們可以在前面的類型簽名代替(->)acat,我們能想到的a -> Bool(->) a Bool,並替換((->) a)f。所以,重寫類型,使用((->) t)來避免與其他類型的變量a衝突:

(<$>) :: (a -> b) -> ((->) t) a -> ((->) t) b 
(<*>) :: ((->) t) (a -> b) -> ((->) t) a -> ((->) t) b 

現在,把東西放回正常綴形式:

(<$>) :: (a -> b) -> (t -> a) -> (t -> b) 
(<*>) :: (t -> (a -> b)) -> (t -> a) -> (t -> b) 

第一個結果是功能組成,你可以從類型中觀察。第二個更復雜,但是再一次,這些類型告訴你你需要什麼 - 它帶有兩個帶有通用類型參數的函數,一個產生函數,另一個產生一個參數傳遞給函數。換句話說,就像\f g x -> f x (g x)。 (這個功能也恰好被稱爲S combinator組合邏輯,這個主題由邏輯學家Haskell Curry廣泛探討,其名字無疑似乎很奇怪!)

(<$>)組合和(<*>)排序的「延伸」,單獨什麼(<$>)確實,在這種情況下,意味着拍攝功能有兩個參數,兩個函數具有共同參數類型,將單個值後兩者,然後將第一個函數應用於兩個結果。所以((&&) <$> (>2) <*> (<7)) x簡化爲(&&) ((>2) x) ((<7) x),或使用正常中綴風格,x > 2 && x < 7。和以前一樣,複合表達式用於以通常的方式過濾列表。


此外,注意,雖然這兩種功能會被混淆到一定程度,一旦你習慣使用的運營商,它們實際上是相當的可讀性。第一個摘要將複合表達式用於多個事物的單個參數,而第二個是標準的「管道」風格的串聯函數組合的標準「管道」風格的一般形式。

就我個人而言,我實際上找到第一個完全可讀的。但我不希望大多數人同意!

+0

感謝您的回答!我讓我很清楚! – 2011-06-09 05:14:05

+0

+1:你比我更好地解釋了我的代碼。 – Landei 2011-06-09 06:53:04

+3

我會補充說,哈斯克勒不會每次都在頭上演示這種類型的體操。相反,模式:'f <$> x <*> y'(也拼寫:'liftA2 f x y')是衆所周知的。這意味着:在兩個Applicative-wrapped值上應用函數f。函數可以被看作是「包裝值」:'(> 2)'和'(<7)'可以被看作是包裝Bools,其中'Num a =>(( - >)a)'是「包裝器」。函數'(&&)'因此可以應用在兩個「包裝」布爾上以產生結果包裝布爾。同樣可以用來定義:'avg = liftA2(/)總長度' – Peaker 2011-06-10 01:07:53

相關問題