1

I'm trying to implement a function which usually puts the first element of a list in a monad, but if the monad is a list it returns the whole list:

putInMonad :: MonadPlus m => [a] -> m a
putInMonad (s:sx) = return s
putInMonad _ = mzero
putInMonad [1,2,3] :: Maybe Int

Should return Just 1, and

putInMonad [1,2,3] :: [] Int

should return [1,2,3].

Any ideas?

duplode
  • 33,731
  • 7
  • 79
  • 150
axyz
  • 13
  • 2
  • You can use `[[]] Int` as lower monad. – Willem Van Onsem Feb 02 '17 at 14:22
  • @WillemVanOnsem No, because the expression `return s` doesn't contain any information about `sx`; simply providing a concrete return type doesn't help that. – chepner Feb 02 '17 at 14:36
  • @chepner: yeah I know it will not behave differently for that specific monad. I only had the idea to solve the problem the other way around: give it a list of lists instead of a list of items. – Willem Van Onsem Feb 02 '17 at 14:38

2 Answers2

5

In your particular use-case, you could take advantage of msum:

putInMonad :: MonadPlus m => [a] -> m a
putInMonad x = msum $ map return x

Then both examples will work:

% putInMonad [1,2,3,4] :: Maybe Int
Just 1
% putInMonad [1,2,3,4] :: [Int]
[1,2,3,4]

Note however that Maybe is not exactly MonadPlus, since mplus there is not associative.

marc
  • 6,103
  • 1
  • 28
  • 33
  • 4
    Any unease/controversy about `MonadPlus` might be avoided by switching to `Alternative` and `asum`, if the `MonadPlus` constraint isn't a hard requirement for the OP. – duplode Feb 02 '17 at 15:15
1

In general, you can't do this, because the function cannot know which Foo instance will be used when the function is called, meaning it has no basis for deciding to return return s or return (s:sx).

In this particular case, there is a workaround (see @marc's answer) when Foo is MonadPlus.

Community
  • 1
  • 1
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Waiting for votes to tell me if I'm technically right, or just flat out wrong. – chepner Feb 02 '17 at 15:16
  • You are not technically right, but merely because the class in question is `MonadPlus`, which offers the workaround shown in marc's answer. That this is impossible in the general case is still a point worth making. I suggest editing your answer to make this subtlety clear and/or point towards marc's answer for the sake of contrast. – duplode Feb 02 '17 at 15:26