Multiple flatMap methods for a single monad?

谁都会走 提交于 2019-12-07 03:29:10

问题


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 binds. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!