[英]Haskell function using Applicative and Functor
我有一個練習題,給我一個函數:
sequence :: Applicative f => [f a] -> f[a]
sequence = foldr (_hole (:)) (pure [])
問題說:
"What type is required for the function that needs to be placed at
_hole in the following expression? Also give a definition for the
expression using and <$> and <*>".
我在理解問題所在時遇到問題。 因此,對於我嘗試過的操作,我假設由於使用了foldr而需要指定運算符,因此我假設其類似於sequence = foldr((+)(:))(pure [])。
然后為表達式的定義,我寫了類似的東西:
sequence :: <*> f => [f a] -> f[a]
sequence = foldr <$> pure []
我很確定我不是100%正確的,因此希望在任何更正上對此有所幫助。
本練習希望您假設某個值_hole
在某處定義,並且上面的代碼進行檢查。 然后,目標是確定_hole
的類型。 然后,它將詢問_hole
的可能定義。
例如,如果我們被給予
foo :: Int
foo = 3 + _hole
答案應該是_hole :: Int
,因為這是使上面的代碼正常工作所需要的。 對於定義, _hole = 2
是可以的。
相反,在
foo :: Int -> Bool
foo = const _hole "hello" True
那么我們需要_hole :: Bool -> Int -> Bool
,例如_hole = \\bi -> b
。
您自己的代碼更加復雜,因此最好寫下所有步驟:
sequence :: Applicative f => [f a] -> f[a]
sequence = foldr (_hole (:)) (pure [])
這里使用文件foldr
,(在列表中)具有類型
foldr :: (b -> c -> c) -> c -> [b] -> c
要輸入檢查,參數必須具有類型
_hole (:) :: b -> c -> c
pure [] :: c
僅使用兩個參數調用的foldr
的結果是
sequence :: [b] -> c
因為這必須與上面的sequence
類型匹配,所以我們得到
[b] = [f a]
c = f [a]
因此, b = fa
和
_hole (:) :: f a -> f [a] -> f [a]
pure [] :: f [a]
pure []
零件類型將按原樣檢查。 另一方面,我們需要
_hole :: (type of (:)) -> f a -> f [a] -> f [a]
即因為對於任何d
(:) :: d -> [d] -> [d]
,我們得到
_hole :: (d -> [d] -> [d]) -> f a -> f [a] -> f [a]
d
可以任意選擇。 但是,選擇d=a
是“自然的”,這樣我們得到
_hole :: (a -> [a] -> [a]) -> f a -> f [a] -> f [a]
現在,我們需要編寫一個定義_hole fxy = ??
用<$>
和<*>
。 本質上,我們需要從庫中重新實現liftA2
。 現在您應該可以解決最后一部分了。
讓我們一步一步地做,逐步發現我們定義中涉及的實體的類型。 我們被給予
sequence :: Applicative f => [f a] -> f [a] -- (1)
sequence = foldr (_hole (:)) (pure []) -- (2)
因此對於某些g
, sequence = mksequence g
g
:
mksequence g xs = foldr (g (:)) (pure []) xs -- (3)
mksequence g [a,b,...,n] = r where -- (4)
r = g (:) a $ g (:) b $ ... $ g (:) n (pure []) -- (5)
mksequence g [a] = g (:) a (pure []) -- (6)
mksequence g [] = pure [] -- (7)
-- [a,b,...,n] :: [f a] <-(4,1) -- (8)
-- a,b,...,n :: f a <-(8) -- (9)
-- r :: f [a] <-(4,1) -- (10)
-- pure [] :: f [a] <-(7,1) -- (11)
-- g (:) :: f a -> f [a] -> f [a] <-(6,8,11,1)
最后,我們找到了g (:)
的類型! 與它比較
(<*>) :: f (a -> t) -> f a -> f t , _A :: f (a -> t)
(_A <*> _C) :: f t , _C :: f a
(_B <*> _C) :: f (t -> s) , _B :: f (a -> (t -> s))
((_B <*> _C) <*> _D) :: f s , _D :: f t
這樣我們就可以了
\ _B _C _D -> ((_B <*> _C) <*> _D)
:: f (a -> (t -> s)) -> f a -> f t -> f s
g ((:) :: a -> [a] -> [a]) :: f a -> f [a] -> f [a]
簽名幾乎匹配! 只需輕輕一點,我們就可以
g (:) = (\ _B _C _D -> ((_B <*> _C) <*> _D)) (pure (:))
因此,概括地說
g f2 fa ft = pure f2 <*> fa <*> ft
因為(<*>)
關聯到左側。 重新檢查類型,
g f2 fa ft = pure f2 <*> fa <*> ft
= f2 <$> fa <*> ft
-- fa :: f a
-- f2 :: a -> t -> s
-- f2 <$> fa :: f (t -> s)
-- ft :: f t
-- (f2 <$> fa) <*> ft :: f s
實際上,此定義已經存在,並被命名為liftA2
用於將二進制函數( f2
)“提升”為適用的“上下文”( f
):
f2 :: a -> t -> s
liftA2 f2 :: f a -> f t -> f s
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.