2017-10-28 27 views
1

FileIO.hsFunctional Programming Course exerciseHaskell語法:函數如何組合在一起?

getFile :: FilePath -> IO (FilePath, Chars) 
getFile = lift2 (<$>) (,) readFile 

一行根據其類型簽名,getFile返回IO (FilePath, Chars),這意味着文件名和它的內容的元組。

但我只是不明白爲什麼它會這樣。

爲什麼FilePath在左側輸出不變,而readFile的文件名填在右邊?

(,)也是一個應用實例嗎? (,)不是IO,那麼lift2擡起了什麼?

而且,有沒有辦法衍生這些類型的簽名並得到證明?

我知道的語法是一個函數遵循它的參數,它在它的右邊吃了一個參數併成爲一個新的函數。但是,當涉及到這樣的代碼,它看起來就像一個魔術立方體給我...

謝謝你幫助我!

Ps。額外信息如下

instance Functor IO where 
    (<$>) = 
    P.fmap 

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

getFiles :: List FilePath -> IO (List (FilePath, Chars)) 
getFiles = sequence . (<$>) getFile 
+1

該文件似乎包含大量的無點式代碼,例如您提到的'getFile'。正如它經常發生的那樣,pointfree導致幾乎模糊的代碼。在這種情況下,我們可以使用有意義的等價''getFile f =(,)f <$> readFile f'',它不僅更清晰,而且更短! Pointfree有時很酷,但不應該爲了自己的利益而不惜一切代價地使用它。 – chi

+0

是的!拉姆達的方式更清晰。因此,在這種情況下,lift2只是提升未知參數,並使表達式無點。現在一切都說得通了。謝謝! –

回答

4

讓我們來看看

lift2 (<$>) (,) readFile 

這的確是簡單的功能應用:

((lift2 (<$>)) (,)) readFile 

(或lift2應用於三個參數)。

涉及(具有唯一地重命名的類型變量減少混淆)的類型是:

lift2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c 
(<$>) :: (Functor g) => (j -> k) -> g j -> g k 
(,) :: m -> n -> (m, n) 
readFile :: FilePath -> IO Chars 

在我們的表達式的第一件事是將lift2(<$>)。這意味着我們需要統一a -> b -> c(類型lift2的第一個參數)和(Functor g) => (j -> k) -> g j -> g k(類型<$>)。

即:

a -> b -> c = (j -> k) -> g j -> g k 
-- where g is a Functor 
a = j -> k 
b = g j 
c = g k 

這工作了。結果類型是

f a -> f b -> f c 
-- where f is an Applicative 

f (j -> k) -> f (g j) -> f (g k) 

現在這個表達式(lift2 (<$>))被施加到(,)。我們不得不再次使類型排隊:

f (j -> k) = m -> n -> (m, n) 

這裏我們使用的財產在->是右結合(即a -> b -> c表示a -> (b -> c)),並且我們可以在類型中使用(curried)前綴表示法(即,a -> b(->) a b相同,其與((->) a) b相同)。

f (j -> k) = ((->) m) (n -> (m, n)) 
f = (->) m 
j = n 
k = (m, n) 

這也可以解決。結果類型是

f (g j) -> f (g k) 

其中(後代)變爲

((->) m) (g n) -> ((->) m) (g (m, n)) 
(m -> g n) -> (m -> g (m, n)) 

該表達式(lift2 (<$>) (,))被施加到readFile。再次,使得類型排隊:

m -> g n = FilePath -> IO Chars 
m = FilePath 
g = IO 
n = Chars 

而代入的結果類型:

m -> g (m, n) 
FilePath -> IO (FilePath, Chars) 

這是整個lift2 (<$>) (,) readFile表達式的類型。正如預期的那樣,它與getFile :: FilePath -> IO (FilePath, Chars)的聲明相符。


但是,我們仍然需要驗證我們的類約束(Functor gApplicative f)得到解決。

gIO,這確實是一個Functor(以及ApplicativeMonad)。這裏沒有什麼大的驚喜。

f更有趣:f = (->) m,所以我們需要尋找一個Applicative實例(->) m。事實上確實存在這種情況,其定義包含對實際做什麼的回答。

我們可以推導出實例必須是什麼樣子通過看的lift2類型(如getFile使用):

lift2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c 
lift2 :: (a -> b -> c) -> ((->) m) a -> ((->) m) b -> ((->) m) c 
lift2 :: (a -> b -> c) -> (m -> a) -> (m -> b) -> (m -> c) 
lift2 :: (a -> b -> c) -> (m -> a) -> (m -> b) -> m -> c 

lift2需要

  • ,結合一個abc功能,
  • ,它可將ma功能,
  • ,它可將mb功能,
  • m,

併產生c

也能做到這一點的唯一方法是通過將m到第二和第三的功能和使用的第一功能結合他們的研究結果:

lift2 f g h x = f (g x) (h x) 

如果我們內聯getFile這個定義,我們得到

getFile = lift2 (<$>) (,) readFile 
getFile = \x -> (<$>) ((,) x) (readFile x) 
getFile = \x -> (,) x <$> readFile x 

鍛鍊; Tibial讀者:

lift2實際上根據<$><*>定義。 實例(->) m中有哪些類型的<$><*>?他們的定義是什麼樣的?

+0

(<*>)fg = \ x - > fx(gx) ..................和(<$>)fg = \ x - > f(gx)... ........ 是嗎?再加上這個,lift2 f g h = \ x - > f(g x)(h x),這三個函數做出某種感覺我還沒有得到... 但是首先,thx爲詳細的解釋和練習! –

+0

@JohnnyLiao看起來正確。 '<*>'也被稱爲S combinator; '<$>'是'。(功能組合)。 – melpomene