我正在阅读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)
但我不明白为什么.能有人带我走过这个吗?:)
我们来看看吧
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)
这f是Maybe,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是人为的:你明确包裹你x的Just.你可以这样做:
fmap ((:) x) (mySafeInit xs)
Run Code Online (Sandbox Code Playgroud)
根据定义fmap,这是Nothing(如果mySafeInit xs是Nothing)或Just ((:) x y)(如果mySafeInit xs是Just y).