<div dir="ltr"><div><div>I happen to support the vast majority of my packages for 3 major GHC releases, not just platforms, and for the current stackage build as well.</div><div><br></div><div>The idea that we can't personally benefit from things for ~3 years because we happen to have packages with long support cycles leads to a fallacy by which by induction no progress can ever be made to benefit the larger pool of Haskell users we will have in the future because we can never pay a price in the short term to benefit them.</div><div><br></div></div><div>Regarding Foldable, there is a chain of reasoning that begins with accepting the Applicative-Monad Proposal:</div><div><br></div><div>Once we accept Applicative as a superclass of Monad then mapM is _never_ the right abstraction.</div><div><br></div><div>Why?</div><div><br></div><div>mapM is needlessly constrained to Monad, so you really start needing the ability to traverse with Applicative.</div><div><br></div><div>mapM actively gets in the way of making code work with the reduced Applicative constraints that are possible post AMP.</div><div><br></div><div>Dead End #1. We can't just generalize the signature of mapM in a way that makes mapM work with Applicative without a whole host of other issues. (mapM is a member of the Traversable class alongside traverse, there exists a corner case where Traversable.mapM can have better termination behavior on a weirdly designed container, so having Prelude mapM and Traversable.mapM generalized differently would be bad.) </div><div><br></div><div>Dead End #2. To go the other way and put in a monomorphized traverse into Prelude would break insane amounts of code across the entire ecosystem.</div><div><br></div><div>To expose traverse is to expose Traversable.</div><div><br></div><div>Exposing Traversable from the Prelude then really wants to drag in the superclasses of Traversable lest you have a class that is exposed in the Prelude with a superclass you can't define. </div><div><br></div><div>Dead End #3. We could have added Applicative as a superclass of Monad without adding it to the Prelude, but it would have been a bad way to proceed. Now users who want to either benefit from the change or define a Monad, ever, would have to add an extra import Control.Applicative line, what is the probem. This is the same argument against forcing Foldable into hiding while possibly exposing Traversable, just mutatis mutandis for Foldable/Applicative and Traversable/Monad.</div><div><br></div><div>Consequently, exposing the pieces you need to define classes that are exposed through Prelude seems to be the direction that in the long term is the most defensible in terms of how to explain the state of the world to newcomers, once we get the hurdle of transition.</div><div><br></div><div>The current state of affairs was quite eloquently summarized by this transcript from Nick Partridge about the process of describing the AMP to his coworkers:<br></div><div><br></div><div><br></div><div><p style="margin:0px 0px 0.357142857142857em;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">In a conversation with co-workers about these changes, I fired up a ghci session to show the difference between<code style="font-family:monospace,monospace;margin:0px 2px;padding:0px 4px;border:1px solid rgb(238,238,210);border-radius:2px;white-space:nowrap;line-height:1em;background-color:rgb(252,252,247)">mapM</code> and <code style="font-family:monospace,monospace;margin:0px 2px;padding:0px 4px;border:1px solid rgb(238,238,210);border-radius:2px;white-space:nowrap;line-height:1em;background-color:rgb(252,252,247)">traverse</code> and what it would mean to generalize the functions in Prelude, and how traverse was just a more general mapM.</p><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Here is how that went down:</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> :i traverse

Top level:
    Not in scope: ‘traverse’
    Perhaps you meant ‘reverse’ (imported from Prelude)
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Ah, of course. I forgot the import.</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> import Data.Traversable
> :i traverse
class (Functor t, Data.Foldable.Foldable t) =>
      Traversable (t :: * -> *) where
  traverse ::
    Control.Applicative.Applicative f => (a -> f b) -> t a -> f (t b)
  ...
    -- Defined in ‘Data.Traversable’
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Great. Now let's look at mapM:</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> :i mapM

Top level:
    Ambiguous occurrence ‘mapM’
    It could refer to either ‘Data.Traversable.mapM’,
                             imported from ‘Data.Traversable’
                          or ‘Prelude.mapM’,
                             imported from ‘Prelude’ (and originally defined in ‘Control.Monad’)
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Oh. Okay we'll check out the one in Prelude by prefixing it.</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> :i Prelude.mapM
Prelude.mapM :: Monad m => (a -> m b) -> [a] -> m [b]
    -- Defined in ‘Control.Monad’
> :t traverse
traverse
  :: (Traversable t, Control.Applicative.Applicative f) =>
     (a -> f b) -> t a -> f (t b)
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Now let's specialise <code style="font-family:monospace,monospace;margin:0px 2px;padding:0px 4px;border:1px solid rgb(238,238,210);border-radius:2px;white-space:nowrap;line-height:1em;background-color:rgb(252,252,247)">traverse</code> for Monad and List, to show that it's equivalent.</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> :t (traverse :: Monad m => (a -> m b) -> [a] -> m [b])

<interactive>:1:2:
    Could not deduce (Control.Applicative.Applicative m1)
      arising from a use of ‘traverse’
    from the context (Monad m)
      bound by the inferred type of
               it :: Monad m => (a -> m b) -> [a] -> m [b]
      at Top level
    or from (Monad m1)
      bound by an expression type signature:
                 Monad m1 => (a1 -> m1 b1) -> [a1] -> m1 [b1]
      at <interactive>:1:2-50
    Possible fix:
      add (Control.Applicative.Applicative m1) to the context of
        an expression type signature:
          Monad m1 => (a1 -> m1 b1) -> [a1] -> m1 [b1]
        or the inferred type of it :: Monad m => (a -> m b) -> [a] -> m [b]
    In the expression:
      (traverse :: Monad m => (a -> m b) -> [a] -> m [b])
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Curses! Looking forward to AMP. Insert explanation about how Applicative should be a superclass of Monad. Pretend that it really is. Wave hands about.</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> :t (traverse :: Applicative m => (a -> m b) -> [a] -> m [b])

<interactive>:1:14:
    Not in scope: type constructor or class ‘Applicative’
</code></pre><p style="margin:0.357142857142857em 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">Okay this is getting silly.</p><pre style="margin-top:0.357142857142857em;margin-bottom:0.357142857142857em;padding:4px 9px;border:1px solid rgb(238,238,210);border-radius:2px;overflow:auto;background-color:rgb(252,252,247)"><code style="font-family:monospace,monospace;margin:0px 2px;border:0px;border-radius:2px;display:block;font-size:1em;line-height:1.42857142857143em;padding:0px!important;background-color:transparent">> import Control.Applicative
> :t (traverse :: Applicative m => (a -> m b) -> [a] -> m [b])
(traverse :: Applicative m => (a -> m b) -> [a] -> m [b])
  :: Applicative m => (a -> m b) -> [a] -> m [b]
</code></pre><p style="margin:0.357142857142857em 0px 0px;padding:0px;font-size:14px;line-height:1.42857142857143em;font-family:verdana,arial,helvetica,sans-serif">This is <em style="font-weight:inherit;margin-left:0px;margin-right:0px;margin-top:0px">really</em> <em style="font-weight:inherit;margin-left:0px;margin-right:0px;margin-bottom:0px">really</em> common teaching/learning scenario using the current state of the Prelude.</p></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Jan 31, 2015 at 12:59 PM, Johan Tibell <span dir="ltr"><<a href="mailto:johan.tibell@gmail.com" target="_blank">johan.tibell@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">I re-read the goals* of the proposal: <a href="https://wiki.haskell.org/Foldable_Traversable_In_Prelude" target="_blank">https://wiki.haskell.org/Foldable_Traversable_In_Prelude</a><div><br></div><div>"One goal here is that people should be able to use methods from these modules without the need of a qualified import -- i.e. to prevent clash in the namespace, by resolving such in favor of the more generalized versions. Additionally, there are some new methods added to the Foldable class because it seems to be the "right" thing."</div><div><br></div><div>Before FTP, I would have to write this to use Foldable/Traversable:</div><div><br></div><div>    import qualified Data.Foldable as F</div><div><br></div><div>The import is qualified as to not collide with the Prelude.</div><div><br></div><div>If I have code that needs to be compatible with more than one GHC release (I typically need compatibility with the last 3 major releases), what do I have to write post-FTP? Since I cannot rely on the Prelude being generalized (because I might be compiling with a pre-FTP compiler) I need to either write:</div><div><br></div><div>    #if MIN_VERSION_base(x,y,z)</div><div>    -- Get Foldable etc from Prelude</div><div>    #else</div><div>    import Data.Foldable (...)</div><div>    import Prelude hiding (...)  -- the same</div><div>    #endif</div><div><br></div><div>Which is terrible. Alternatively I can write<br></div><div><br></div><div>    import qualified Data.Foldable as F<br></div><div><br></div><div>but now nothing is gained over the pre-FTP state. Only after 3+ years (at the current GHC release phase) I can drop that one extra import. One out of perhaps 20. That seems quite a small gain given that we will then have made Data.List a very confusing module (it's essentially Data.Foldable under a different name), broken some code due to added type ambiguity, and also removed one of the simpler ways to remove that ambiguity, which would have been to import one of the monomorphic list functions that no longer exist.</div><div><br></div><div><div>* People tell me that there are other goals, but they aren't really stated clearly anywhere.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>-- Johan</div><div><br></div><div><br></div></font></span></div></div>
</blockquote></div><br></div>