How can I apply the (a -> b) function to a without having to unwrap (to use the Rust term) and re-wrap it? I’ve come up with these solutions, which I believe are functionally identical, but it feels like there’s probably a cleaner way to do it without having to use Just/return/pure, etc., as well as propagating a Nothing value through it. This is certainly not a showstopper for me, I’m just trying to become more familiar and comfortable with the tools at my disposal for dealing with monads/applicative functors.
thing :: Maybe (a -> b) -> a -> Maybe b
thing (Just op) = Just . op
thing1 :: Maybe (a -> b) -> a -> Maybe b
thing1 maybeOp y = maybeOp >>= \op -> Just $ op y
thing2 :: Maybe (a -> b) -> a -> Maybe b
thing2 maybeOp = ap maybeOp . Just
Yes, it is “just” sectioning of the $ function/operator; i.e. $ has type (a → b) → a → b, so partially applying it/using sectioning (in the same way that you can write (+3) to mean the equivalent of \x → x + 3, you can write ($ x), if you have some x of type a.
I did do that, actually. Then I saw that it lead to some non-“standard” package and didn’t go any further. I should’ve realized that it would still make sense to look at the source. Thanks.
I’m not getting what you think you might be avoiding. There’s no notion of Haskell “re-wrapping” something and somehow re-using an updated location: always Haskell builds a new value/no sharing. And in your case, the return value is a different type, so definitely no attempt to re-anything.
The solutions you suggest to elide an argument, and those others have given are perhaps neater code (debatable), but they’re going to compile down (after eta-expansion) to the same:
thinge (Just op) y = Just $ op y
I think the other two (thing1, thing2) are also partial. They want to apply an explicit Just on RHS, so they need an explicit (Just f) on LHS.
The fmap solution, because it’s looking for a Functor will take Nothing to Nothing, but that’s a happy accident of wanting to take Maybe to Maybe.
thing3 maybeOp x = maybeOp >>= \op -> return (op x) -- everything to the right of the ( >>= ) is our (a -> mb function); in this cause, our `a` is actually an `a -> b` so we need to apply it to an `a` before using pure/return to make it an `mb`.
And this will also help you with the Nothing case since:
( >>= ) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing
Maybe x >>= f = f x
Also you have applicative here where you could do:
thing4 maybeOp x = maybeOp <*> pure x -- (pure x because we're lifting our normal value into the context it needs: in this case, a `Just x`).
since
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
Nothing <*> _ = Nothing
_ <*> Nothing = Nothing
Just ab <*> Just a = Just (ab a)
Essentially, if you don’t want to unwrap values, you can use functor, applicative or monad to act on results.
Functor for when you just want to change a value in a context.
Applicative for when you want to combine many values or results together into a new structure.
Monad for when you want to take the result of a computation and then apply it to a function that then produces a new result or not.
For this reason, I would choose Functor in your case. The applicative function I gave is just the Homomorphism law for applicative.
Thank you for the reply. I’m not well-enough versed in Haskell to know exactly which terms to use and when, so my “unwrap/rewrap” reference was just my attempt at explaining what I’m trying to do using the Rust terminology I knew. Having to write Just in the pattern matching to get at the internal value only to write Just again to put the resulting value into a Maybe seemed redundant. I’m mostly just trying to expand my knowledge on the different ways to handle these situations and not necessarily, in this case, try to do what is the most practical or most pragmatic. This thread has helped on all fronts!