讓我們來看看
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 g
,Applicative f
)得到解決。
g
是IO
,這確實是一個Functor
(以及Applicative
和Monad
)。這裏沒有什麼大的驚喜。
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
需要
- ,結合一個
a
和b
成c
功能,
- ,它可將
m
成a
功能,
- ,它可將
m
成b
功能,
- 和
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
中有哪些類型的<$>
和<*>
?他們的定義是什麼樣的?
該文件似乎包含大量的無點式代碼,例如您提到的'getFile'。正如它經常發生的那樣,pointfree導致幾乎模糊的代碼。在這種情況下,我們可以使用有意義的等價''getFile f =(,)f <$> readFile f'',它不僅更清晰,而且更短! Pointfree有時很酷,但不應該爲了自己的利益而不惜一切代價地使用它。 – chi
是的!拉姆達的方式更清晰。因此,在這種情況下,lift2只是提升未知參數,並使表達式無點。現在一切都說得通了。謝謝! –