Cases in which we shall not use monadic bind to write mfix down using loop

有些话、适合烂在心里 提交于 2021-02-07 13:22:53

问题


I have been trying to write mfix down using Control.Arrow.loop. I came up with different definitions and would like to see which one is mfix's actual workalike.

So, the solution I reckon to be the right one is the following:

mfix' :: MonadFix m => (a -> m a) -> m a
mfix' k = let f ~(_, d) = sequenceA (d, k d)
          in (flip runKleisli () . loop . Kleisli) f

As one can see, the loop . Kleisli's argument works for Applicative instances. I find it to be a good sign as we mostly have our knot-tying ruined by (>>=)'s strictness in the right argument.

Here is another function. I can tell that it is not mfix's total workalike, but the only case I found is not very natural. Take a look:

mfix'' k = let f ~(_, d) = fmap ((,) d) (return d >>= k)
           in (flip runKleisli () . loop . Kleisli) f

As far as I understand, not every strict on the right-hand bind forces its argument entirely. For example, in case of IO:

GHCi> mfix'' ((return :: a -> IO a) . (1:))
[1,1,1,1,1,Interrupted.

So, I decided to fix this. I just took Maybe and forced x in Just x >>= k:

data Maybe' a = Just' a | Nothing' deriving Show

instance Functor Maybe' where
    fmap = liftM

instance Applicative Maybe' where
    pure  = return
    (<*>) = ap

instance Monad Maybe' where
    return         = Just'
    Nothing' >>= k = Nothing'
    Just' x  >>= k = x `seq` k x

instance MonadFix Maybe' where
    mfix f = let a = f (unJust' a) in a
             where unJust' (Just' x) = x
                   unJust' Nothing'  = errorWithoutStackTrace "mfix Maybe': Nothing'."

Having this on our hands:

GHCi> mfix ((return :: a -> Maybe' a) . (1:))
[1,1,1,1,1,Interrupted.

GHCi> mfix' ((return :: a -> Maybe' a) . (1:))
[1,1,1,1,1,Interrupted.

GHCi> mfix'' ((return :: a -> Maybe' a) . (1:))
Interrupted.

So, here are my questions:

  1. Is there any other example which could show that mfix'' is not totally mfix?
  2. Are monads with such a strict bind, like Maybe', interesting in practice?
  3. Are there any examples which show that mfix' is not totally mfix that I have not found?

A small side note on IO:

mfix3 k' = 
    let 
       k = return . k'
       f ~(_, d) = fmap ((,) d) (d >>= k)
    in (join . flip runKleisli () . loop . Kleisli) f

Do not worry about all the returns and joins - they are here just to have mfix3's and mfix's types match. The idea is that we pass d itself instead of return d to the (>>=) on the right-hand. It gives us the following:

GHCi> mfix3 ((return :: a -> IO a) . (1:))
Interrupted.

Yet, for example (thanks to Li-yao Xia for their comment):

GHCi> mfix3 ((return :: a -> e -> a) . (1:)) ()
[1,1,1,1,1,Interrupted.

Edit: thanks to HTNW for an important note on pattern-matching in the comments: it is better to use \ ~(_, d) -> ..., not \ (_, d) -> ....


回答1:


Here's a partial answer, which I hope is better than no answer.

Is there any other example which could show that mfix'' is not totally mfix?

We can distinguish mfix'' from mfix also by making return strict instead of (>>=).

Are monads with such a strict bind, like Maybe', interesting in practice?

Probably not. (Questions about the existence of "practical" examples are not easy to answer negatively.)

Containers that are strict in their elements might be an example of this. (In case you're wondering about the official containers package, it does not actually define Monad instances for Map and IntMap, and the Monad instance of Seq is lazy in the elements of the sequence).

Note also that it is unclear whether the monad laws take strictness into account. If you do, then such things are not lawfully monads because they break the left identity law: (return x >>= k) = k x for x = undefined.

Are there any examples which show that mfix' is not totally mfix that I have not found?

If you take the definition of loop in the standard library, in terms of mfix, then I think that mfix' = mfix, though I couldn't complete a proof (I could either be missing a good trick, or there's a missing MonadFix law).

The main point of contention, as was hinted at in the comments, is strictness. Both your definition of mfix' and the standard library's definition of loop are careful to expand the argument function to be lazier (using lazy patterns (~(_, d)) and snd respectively; the two techniques are equivalent). mfix and mfix' are still equal if exactly one of those precautions is dropped. There is a mismatch (mfix /= mfix') if both are dropped.



来源:https://stackoverflow.com/questions/63093464/cases-in-which-we-shall-not-use-monadic-bind-to-write-mfix-down-using-loop

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