问题
Does it make sense to define multiple flatMap
(or >>=
/ bind
in Haskell) methods in a Monad?
The very few monads I actually use (Option
, Try
, Either
projections) only define one flatMap method.
For exemple, could it make sense to define a flatMap
method on Option
which would take a function producing a Try
? So that Option[Try[User]]
would be flattened as Option[User]
for exemple? (Considering loosing the exception is not a problem ...)
Or a monad should just define one flatMap
method, taking a function which produces the same kind of monad? I guess in this case the Either
projections wouldn't be monads? Are they?
回答1:
I have once seriously thought about this. As it turns out, such a construct (aside from losing all monadic capabilities) is not really interesting, since it is sufficient to provide a conversion from the inner to the outer container:
joinWith :: (Functor m, Monad m) => (n a -> m a) -> m (n a) -> m a
joinWith i = join . (fmap i)
bindWith :: (Functor m, Monad m) => (n a -> m a) -> m a -> (a -> n a) -> m a
bindWith i x f = joinWith i $ fmap f x
*Main> let maybeToList = (\x -> case x of Nothing -> []; (Just y) -> [y])
*Main> bindWith maybeToList [1..9] (\x -> if even x then Just x else Nothing)
[2,4,6,8]
回答2:
It depends what "make sense" means.
If you mean is it consistent with the monad laws, then it's not exactly clear to me the question entirely makes sense. I'd have to see a concrete proposal to tell. If you do it the way I think you suggest, you'll probably end up violating composition at least in some corner cases.
If you mean is it useful, sure, you can always find cases where such things are useful. The problem is that if you start violating monad laws, you have left traps in your code for the unwary functional (category theory) reasoner. Better to make things that look kind of like monads actually be monads (and just one at a time, though you can provide an explicit way to switch a la Either
--but you're right that as written LeftProjection
and RightProjection
are not, strictly speaking, monads). Or write really clear docs explaining that it isn't what it looks like. Otherwise someone will merrily go along assuming the laws hold, and *splat*.
回答3:
flatMap
or (>>=)
doesn't typecheck for your Option[Try[ ]]
example. In pseudo-Haskell notation
type OptionTry x = Option (Try x)
instance Monad OptionTry where
(>>=) :: OptionTry a -> (a -> OptionTry b) -> OptionTry b
...
We need bind
/flatMap
to return a value wrapped in the same context as the input value.
We can also see this by looking at the equivalent return
/join
implementation of a Monad. For OptionTry
, join
has the specialized type
instance Monad OptionTry where
join :: OptionTry (OptionTry a) -> OptionTry a
...
It should be clear with a bit of squinting that the "flat" part of flatMap
is join
(or concat
for lists where the name derives from).
Now, it's possible for a single datatype to have multiple different bind
s. Mathematically, a Monad is actually the data type (or, really, the set of values that the monad consists of) along with the particular bind
and return
operations. Different operations lead to different (mathematical) Monads.
回答4:
It doesn't make sense, for a specific data type, as far as I know, you can only have one definition for bind
.
In haskell a monad is the following type class,
instance Monad m where
return :: a -> m a
bind :: m a -> (a -> m b) -> m b
Concretely For list Monad we have,
instance Monad [] where
return :: a -> [] a
(>>=) :: [] a -> (a -> [] b) -> [] b
Now let's consider a monadic function as.
actOnList :: a -> [] b
....
A Use case to illustrate,
$ [1,2,3] >>= actOnList
On the function actOnList
we see that a list is a polymorphic type constraint by another type (here []
). Then when we speak about the bind operator for list monad we speak about the bind operator defined by [] a -> (a -> [] b) -> [] b
.
What you want to achieve is a bind
operator defined like [] Maybe a -> (a -> [] b) -> [] b
, this not a specialize version of the first one but another function and regarding it type signature i really doubt that it can be the bind
operator of any kind of monad as you do not return what you have consumed. You surely go from one monad to another using a function but this function is definitely not another version of the bind
operator of list.
Which is why I've said, It doesn't make sense, for a specific data type, as far as I know, you can only have one definition for bind
.
来源:https://stackoverflow.com/questions/16407356/multiple-flatmap-methods-for-a-single-monad