Learn You a Haskell提供以下练习:
让我们尝试实现一个带有一个应用程序和列表的函数 返回一个以列表作为结果值的应用程序。
LYAH给出了类型签名sequenceA' :: (Applicative f) => [f a] -> f [a]
我从Applicative
开始,但不确定如何以a
的方式从f a
中提取所有Applicative
的{{1}}。
所以,我将其实现为Maybe
。当然,对于所有'Applicative'来说,这都是不可接受的。
import Control.Applicative
sequenceA' :: (Num a) => [Maybe a] -> Maybe [a]
sequenceA' [] = pure []
sequenceA' xs = pure $ [extract' x | x <- xs ]
extract' :: (Num a) => Maybe a -> a
extract' x = case x of
Just y -> y
Nothing -> 0
如何从a
f a
f
中提取Applicative
?
答案 0 :(得分:10)
你不能一般。实际上,您提供的Maybe
示例是一个很好的示例,因为它要求它是Num
实例。对于Maybe
Applicative
一般来说没有意义,所以这将是一般解决方案的反例。另一个反例是IO
。 extract
没有有效的IO
实施。
要创建一个足以使用所有Applicative
个实例的函数,您必须能够仅使用Applicative
及其超类Functor
中的方法构造函数。 。没有办法extract
仅使用fmap
,pure
和(<*>)
。
答案 1 :(得分:6)
没有必要从应用仿函数中取出来实现这一点。
关于applicative functors的好处是它们允许你在每个应用计算的结果上使用普通函数,所以
如果你有c1
,c2
和c3
类型f a
,f b
,f c
的申请人
产生v1
,v2
和v3
类型的a
,b
和c
值,
但实际上你想在值上使用函数g :: a -> b -> c -> d
生成g v1 v2 v3 :: d
,然后就可以了
g <$> c1 <*> c2 <*> c3
,其类型为f d
。
因此,我们可以使用函数(:)将我们的应用程序列表中的第一个值与其余值一起加入,这样您就可以执行(:) <$> thingThatGivesFirstValue <*> thing that gives the rest of the list
之类的操作。因此,如果您在应用程序列表上进行模式匹配,那么它将是一个很好的递归。
sequenceA' :: (Applicative f) => [f a] -> f [a]
sequenceA' [] = -- just give the empty list
sequenceA' (c:cs) = -- hmm. What could go here then?
所以你应该得到
ghci> sequenceA' [getLine, getLine, getLine]
Hello
there
everyone
["Hello","there","everyone"]
ghci> sequenceA' [Just 3, Just 4, Just 5]
Just [3,4,5]
这是一个示例函数,可以帮助您处理recursice案例:
nth :: Applicative f => f Int -> f [a] -> f a
nth wrappedInt wrappedList = (!!) <$> wrappedInt <*> wrappedList
所以你不需要打开任何东西或取出价值,操作员<$> and <*>
可以让你在里面做你喜欢的事。
nth (Just 3) (Just "Hello") == 'l'
答案 2 :(得分:2)
这是一个提示:
foo :: Applicative f => f Int -> f Int -> f Int
foo fx fy = (+) <$> fx <*> fy -- apply + "under" the functor
bar :: Applicative f => f a -> f [a] -> f [a]
bar fx fxs = ??? <$> fx <*> fxs -- apply ??? "under" the functor
sequenceA' :: Applicative f => [f a] -> f [a]
sequenceA' [] = pure [] -- as in your solution
sequenceA' (x:xs) = let y = x -- :: f a
ys = sequenceA' xs -- :: f [a]
in ???
我在最后一个函数中使用let
来阐明所涉及的类型。填写???
后,您当然可以删除let
。
答案 3 :(得分:0)
如果a
不是IO
f a
中提取f
import Control.Applicative
import System.IO
import Control.Monad.ST
-- with AndrewC sequenceA' definition
sequenceA' :: (Applicative f) => [f a] -> f [a]
sequenceA' [] = pure []
sequenceA' (c:cs) = (:) <$> c <*> sequenceA' cs
seqOfMaybes :: [Maybe a] -> [a]
seqOfMaybes listOfMb = case sequenceA' listOfMb of
Nothing -> []
Just list -> list
-- sequencing ST computations
compute :: a -> ST s a
compute x = return x
seqOfSTData :: [Int] -> [Int]
seqOfSTData vals = runST $ (sequenceA' (map compute vals) :: ST s [Int])
-- but you cannot escape the applicative f if f is IO
readAnInt :: IO Int
readAnInt = putStrLn "give me an Int>" >> hFlush stdout >> getLine >>= (return . read)
seqOfIO :: [IO a] -> IO [a]
seqOfIO listOfIO = sequenceA' listOfIO
main :: IO ()
main = do
print $ seqOfMaybes [Just 3, Just 4, Just 5]
print $ seqOfSTData [1,2,3]
seqOfIO [readAnInt, readAnInt] >>= print