import Control.Applicative
import Control.Arrow
filter ((&&) <$> (>2) <*> (<7)) [1..10]
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10]
兩者都得到相同的結果!但是,我很難理解。這裏有人能詳細解釋嗎?你介意在論壇中解釋代碼嗎?
import Control.Applicative
import Control.Arrow
filter ((&&) <$> (>2) <*> (<7)) [1..10]
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10]
兩者都得到相同的結果!但是,我很難理解。這裏有人能詳細解釋嗎?你介意在論壇中解釋代碼嗎?
讓我們從第二個開始,這更簡單。我們有兩個神祕的運營商在這裏,有以下幾種類型:
(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')
(>>>) :: Category cat => cat a b -> cat b c -> cat a c
的Arrow
和Category
型類主要是對的事情,表現得像功能,其中當然也包括函數本身,而且這裏兩個實例都只是普通的(->)
。所以,重寫類型以使用:
(&&&) :: (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
。這些如何兼容?
答案是,正如我們可以在前面的類型簽名代替(->)
爲a
和cat
,我們能想到的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
。和以前一樣,複合表達式用於以通常的方式過濾列表。
此外,注意,雖然這兩種功能會被混淆到一定程度,一旦你習慣使用的運營商,它們實際上是相當的可讀性。第一個摘要將複合表達式用於多個事物的單個參數,而第二個是標準的「管道」風格的串聯函數組合的標準「管道」風格的一般形式。
就我個人而言,我實際上找到第一個完全可讀的。但我不希望大多數人同意!
感謝您的回答!我讓我很清楚! – 2011-06-09 05:14:05
+1:你比我更好地解釋了我的代碼。 – Landei 2011-06-09 06:53:04
我會補充說,哈斯克勒不會每次都在頭上演示這種類型的體操。相反,模式:'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
這些例子來自哪裏? – 2011-06-09 03:33:57
應用程序確實需要自己的語法糖,適用於普通應用程序和中綴應用程序。 '過濾器((> 2)<(&&)>(<7))[1..10]'會很好地讀取。 – 2011-06-09 03:44:44
@pelotom:總會有成語括號。我們現在有monad理解,爲什麼不能應用理解? :) – 2011-06-09 03:58:24