当两者都在Maybes中时,如何将值连接到列表?

Doe*_*Joe 1 haskell

我正在阅读realworldhaskell,我来到第4章,书中谈到了"安全,谨慎地处理崩溃的功能".我正在尝试写一个安全版本init :: [a] -> [a].这是我得到的:

mySafeInit :: [a] -> Maybe [a]
mySafeInit [] = Nothing
mySafeInit (_:[]) = Just []
mySafeInit (x:xs) =  ???
Run Code Online (Sandbox Code Playgroud)

我知道我做不了什么x : mySafeInit xs,因为那就像做a : Just [a](对吧?).我已经读过了fmap,所以我试过这个:

mySafeInit :: [a] -> Maybe [a]
mySafeInit [] = Nothing
mySafeInit (_:[]) = Just []
mySafeInit (x:xs) =  fmap (:) (Just x) (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

那保证会起作用吧?不,我正在努力理解为什么.我有一个Just a和一个Just [a],对吧?我不应该使用fmap它,给它功能(:)和我的两个maybes,然后Just [a]回来?

经过一些谷歌搜索,我确实发现这是有效的:

mySafeInit :: [a] -> Maybe [a]
mySafeInit [] = Nothing
mySafeInit (_:[]) = Just []
mySafeInit (x:xs) =  fmap (:) (Just x) <*> (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

但我不明白为什么.能有人带我走过这个吗?:)

mel*_*ene 7

我们来看看吧

fmap (:) (Just x) (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

因为Maybe,fmap被定义为

fmap _ Nothing = Nothing
fmap f (Just x) = Just (f x)
Run Code Online (Sandbox Code Playgroud)

因此

fmap (:) (Just x)
Run Code Online (Sandbox Code Playgroud)

Just ((:) x)
Run Code Online (Sandbox Code Playgroud)

请注意,这不是一个功能; 它Maybe是某种类型的值(事实上,它是Maybe ([a] -> [a])).这就是为什么

(Just ((:) x)) (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

是类型错误(您只能应用函数).

在代码的第二个版本中,您做到了

fmap (:) (Just x) <*> (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

,是的

(Just ((:) x)) <*> (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

看看类型<*>,我们得到:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

fMaybe,a而且b都是[a]:

(<*>) :: Maybe ([a] -> [a]) -> Maybe [a] -> Maybe [a]
Run Code Online (Sandbox Code Playgroud)

这就是这个版本检查出来的原因.

我不应该使用fmap它,给它功能(:)和我的两个maybes,然后Just [a]回来?

不,因为fmap只能使用一个参数的函数.但是,有两个参数的另一个函数:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
Run Code Online (Sandbox Code Playgroud)

对比

fmap :: (Functor f) => (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

liftA2 (:) (Just x) (mySafeInit xs) 应该确实有效.

但它不必要地复杂化,因为你的一个maybes是人为的:你明确包裹你xJust.你可以这样做:

fmap ((:) x) (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)

根据定义fmap,这是Nothing(如果mySafeInit xsNothing)或Just ((:) x y)(如果mySafeInit xsJust y).