问题
I wrote this function:
appFunc :: Integer -> Integer -> Bool -> Maybe (Integer,Integer)
appFunc i1 i2 b = if b then Just (i1,i2) else Nothing
And then I use it as such in GHCi:
> appFunc <$> Just 3 <*> Nothing <*> Just True
Nothing
Which is great because if at least one of the parameters is Nothing
then the whole expression evaluates to Nothing
. However, when all parameters are Just
then I get a nested Maybe
:
> appFunc <$> Just 3 <*> Just 1 <*> Just False
Just Nothing
Ideally, I would like it to evaluate to plain old Nothing
. So my solution was to use join
:
> join $ appFunc <$> Just 3 <*> Just 1 <*> Just True
Just (3,1)
Is there a better solution or cleaner style? I was experimenting with the monad >>=
function but with no success. For example I tried writing:
> Just True >>= appFunc <$> Just 3 <*> Just 1
* Couldn't match expected type `Bool -> Maybe b'
with actual type `Maybe (Bool -> Maybe (Integer, Integer))'
* Possible cause: `(<*>)' is applied to too many arguments
In the second argument of `(>>=)', namely
`appFunc <$> Just 5 <*> Just 4'
In the expression: Just True >>= appFunc <$> Just 5 <*> Just 4
In an equation for `it':
it = Just True >>= appFunc <$> Just 5 <*> Just 4
* Relevant bindings include
it :: Maybe b (bound at <interactive>:51:1)
This error makes sense to me because:
appFunc <$> Just 3 <*> Just 1 :: m (a -> m b)
whereas >>= :: m a -> (a -> m b) -> m b
Is there a monad solution or should I just stick to the applicative style with join
?
回答1:
Why not just
module Main where
import Data.Bool
appFunc :: Integer -> Integer -> Bool -> Maybe (Integer, Integer)
appFunc i1 i2 what = bool Nothing (Just (i1,i2)) what
result = do
i1 <- Just 1
i2 <- Just 2
test <- Just True
appFunc i1 i2 test
result2 = Just 1 >>= \i1 -> Just 2 >>= \i2 -> Just True >>= appFunc i1 i2
main = do
print result
print result2
Your appFunc
is more like a typical monadFunc
. As duplode already mentioned, using join
is just a monad solution, I just rephrased that into the more idiomatic style.
To get a better intuition of those things, let's look at the signature of the central applicative operation
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
All three parameters to (<*>)
are applicatively wrapped values and (<*>)
knows the "wrapping" before it needs to peak into it, to do something with them. For example
Just (+1) <*> Just 5
here, the calculation involving the "wrapped" function (+1)
and the "wrapped" value 5
can't alter the "wrapping" Just
in this case.
Your appFunc
on the other hand, needs pure values to produce something in a "wrapping". That's not applicative. Here we need to do some calculations with the values, to know, what a constituent part of the "wrapping" will be.
Let's look at the central monadic operation:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Here the second parameter does exactly that. It is a function taking a pure value and returning something in a wrapping. Just like appFunc i1 i2
.
来源:https://stackoverflow.com/questions/52906712/question-about-applicative-and-nested-maybe