#4148: improve new recursive do syntax
---------------------------------+------------------------------------------
    Reporter:  guest             |       Owner:                  
        Type:  feature request   |      Status:  new             
    Priority:  low               |   Milestone:  7.6.1           
   Component:  Compiler          |     Version:  6.12.3          
    Keywords:                    |          Os:  Unknown/Multiple
Architecture:  Unknown/Multiple  |     Failure:  None/Unknown    
  Difficulty:                    |    Testcase:                  
   Blockedby:                    |    Blocking:                  
     Related:                    |  
---------------------------------+------------------------------------------

Comment(by lerkok):

 Apologies for the rather long comment below. It's my personal view and I'm
 probably a bit biased towards the very original translation of mdo-
 expressions for historical reasons. I'm hoping others will find it useful
 in shedding some light onto at least the original motivations for the
 segmentation aspect of mdo, which seems to be the main source of
 discontent here..

 Segmentation was part of the very original transformation proposed for mdo
 expressions for a very simple reason: The right-shrinking property of mfix
 only holds for very few monads of practical interest. Without right-
 shrinking, the knot-tying loop implied by mfix cannot be loosened on the
 right without changing termination properties. This aspect is well studied
 in the literature: In fact, there's a very generic theorem stating that no
 monad based on a disjoint sum type admits a value recursion operator that
 satisfies the right-shrinking property. (See proposition 3.1.6 of
 http://goo.gl/Dj6CO, for instance.) A similar concern arises in the
 context of traces over premonoidal categories, as found in Nick Benton's
 work, and also in Ross Patterson's arrow-fix axiomatization; for instance
 when one considers the fixed-point operator for he  Kleisli arrow of a
 monad, as derived from the mfix of the underlying monad.

 The question then becomes what is the user intended meaning of such
 expressions, when used with a monad that doesn't satisfy the right-
 shrinking property? There's a rough analogy here with typing of recursive
 binding groups: When a recursive binding group is typed (i.e., a let/where
 expression), Haskell says that recursively dependent groups must be
 identified and typed separately. This improves polymorphism. If you don't
 do the grouping, then you'll get less polymorphic types. Not doing this
 would be confusing for the user, since seemingly irrelevant bindings in a
 group shouldn't change what types are assigned to other bindings. If you
 don't do this "segmentation" during typing, then you'll get less
 polymorphic types, which would be arguably not what the user wanted in the
 first place.

 We (John Launchbury and myself) had precisely the same motivation when the
 original mdo was designed: If there's a recursive binding group, then the
 "knot" should be tied over the minimal segment necessary to resolve the
 recursive binding. If you do not do this, then textually irrelevant
 recursive bindings can change the meaning of one another, which can be
 quite confusing for the user. (There's a simple example of this in Section
 7.2.2 of http://goo.gl/Dj6CO.) So, in this sense, grouping of recursive
 bindings for typing purposes in ordinary let/where expressions and
 grouping of recursive segments in an mdo-expression serve the same purpose
 of capturing the alleged user intent as closely as possible.

 One valid argument against the above claim could be that the polymorphic
 type improvement is a change to the static semantics, while the
 segmentation in mdo is a change to the dynamic semantics. This is true.
 However, the point remains that language constructs should reflect what
 the user intends to say.

 Furthermore, I see no reason why an mdo expression should say that all the
 bindings should be in one big knot-tying loop; Just like I wouldn't expect
 GHC to wrap around one big call to "fix" around any purely recursive group
 of definitions. Recall that the good old "fix" is *the* mfix for the
 identity monad, and when we write a recursive "let" binding, we can
 consider it as an mdo-binding over the identity monad. Just like we'd
 fully expect to see separate fix'es around different recursive bindings
 when we write code in the identity monad implicitly provided by let/where
 bindings, I'd expect to see separate fix'es (i.e., mfix calls) when I
 happen to use some other monad. That is, I don't see why other monads
 should be discriminated, as compared to the identity monad.

 (The same line of thought would also say we should have just one "do"
 expression that handles both recursive and non-recursive variants, just
 like we do not have separate let/letrec expressions in Haskell. However, I
 think changing "do" into a construct that also handles recursion is
 unrealistic for practical reasons. In particular, due to shadowing, the
 change wouldn't be backwards compatible.)

 One possible compromise might have been the following: Keep both mdo-
 expressions (which will do segmentation), and "rec" segments, which will
 not do segmentation. Unfortunately, mdo was removed from GHC a while ago
 as Simon commented above; so bringing it back is probably out of question.
 Given that, I'd think we should simply let "rec" do the segmentation as it
 does currently, giving us the rough equivalence that we can replace "mdo"
 with "do rec" (modulo the issue with the last expression has to be
 indented appropriately), and keep the segmentation semantics present as
 implied by the original mdo. If the user really wants to control where
 recursion takes place, then he/she can insert explicit calls to mfix as
 appropriate. In other words, if "rec" doesn't do segmentation, then
 writing the corresponding "mfix" is probably not asking too much either,
 so we should maybe completely drop the whole "rec" business and just live
 with explicit mfix calls.

 Having said all this, I also think that most of this discussion is rather
 moot as well. I'm yet to see anyone bitten by whether mdo (or rec) does
 segmentation or not. I believe that segmentation is rather necessary
 purely from a language design point of view for doing the right thing,
 i.e., matching the user intended semantics. But it's not the end of the
 world if we go the other way. Bringing back the good old "mdo" expresions
 would make me happy, but I can't say that it's a high priority by any
 means.

 Again, apologies for the long post and thanks for reading through my
 personal account of the matter. To state my position on this matter, I
 think the best solution would be to bring back mdo-expressions with full
 segmentation, and keep "rec" as well, which doesn't do any segmentation.
 However, it sounds like bringing "mdo" back in GHC is quite unlikely, so I
 think "rec" should keep doing segmentation so we have at least the rough
 correspondence "mdo = do rec".

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/4148#comment:11>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler

_______________________________________________
Glasgow-haskell-bugs mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs

Reply via email to