[GHC] #15034: Desugaring `mdo` moves a `let` where it shouldn't be

GHC ghc-devs at haskell.org
Mon Apr 16 18:58:59 UTC 2018


#15034: Desugaring `mdo` moves a `let` where it shouldn't be
-------------------------------------+-------------------------------------
        Reporter:  parsonsmatt       |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:  8.6.1
       Component:  Compiler          |              Version:  8.2.2
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by parsonsmatt):

 I should have used a less minimal example, I think I didn't adequately
 demonstrate the problem :) I ran into this while working on a DSL for
 database definitions, and I'm using `RecursiveDo` to allow table
 references out of order. I am also using an `ST` token trick to ensure
 that field references don't escape their table.

 Here's a snippet of the DSL:

 {{{#!hs
 example :: Schema
 example = mdo
     let theUsual = derive @[Eq, Ord, Show]
     table_ "Dog" $ do
         owner <- field "owner" userRef
         field            "name" (typ @String)

         foreignKey userRef "fk_dog_user" [owner]

         derive @[Eq, Ord, Show]

     userRef <- table "User" $ do
         field "name" (typ @String)
         field "age" (typ @Int)
             ! "do people use attribute?"
             ! "i hope so"

         theUsual

     table_ "Friend" $ do
         _    <- field "foobar" (typ @Int)
         name <- field "name" (typ @Int)
         ugh  <- field "ugh" (typ @Int)

         unique "FriendName" name
         primary ugh

         theUsual
 }}}

 This works fine. However, if I move `theUsual` definition to below the
 `table_ "Dog"` block:

 {{{#!hs
 example = mdo
     table_ "Dog" $ do
         owner <- field "owner" userRef
         field            "name" (typ @String)

         foreignKey userRef "fk_dog_user" [owner]

         derive @[Eq, Ord, Show]

     let theUsual = derive @[Eq, Ord, Show]

     userRef <- table "User" $ do
         field "name" (typ @String)
         field "age" (typ @Int)
             ! "do people use attribute?"
             ! "i hope so"

         theUsual

     table_ "Friend" $ do
         _    <- field "foobar" (typ @Int)
         name <- field "name" (typ @Int)
         ugh  <- field "ugh" (typ @Int)

         unique "FriendName" name
         primary ugh

         theUsual
 }}}

 I get the following error:

 {{{
 /home/matt/Projects/mesa-verde/src/MesaVerde/Internal.hs:59:9: error:
     • Couldn't match type ‘s0’ with ‘s’
         because type variable ‘s’ would escape its scope
       This (rigid, skolem) type variable is bound by
         a type expected by the context:
           forall s. EntityDefine s ()
         at src/MesaVerde/Internal.hs:(51,5)-(59,16)
       Expected type: EntityDefine s ()
         Actual type: EntityDefine s0 ()
     • In a stmt of a 'do' block: theUsual
       In the second argument of ‘($)’, namely
         ‘do _ <- field "foobar" (typ @Int)
             name <- field "name" (typ @Int)
             ugh <- field "ugh" (typ @Int)
             unique "FriendName" name
             ....’
       In a stmt of an 'mdo' block:
         table_ "Friend"
           $ do _ <- field "foobar" (typ @Int)
                name <- field "name" (typ @Int)
                ugh <- field "ugh" (typ @Int)
                unique "FriendName" name
                ....
     • Relevant bindings include
         ugh :: FieldRef s (bound at src/MesaVerde/Internal.hs:54:9)
         name :: FieldRef s (bound at src/MesaVerde/Internal.hs:53:9)
         theUsual :: EntityDefine s0 ()
           (bound at src/MesaVerde/Internal.hs:41:9)
    |
 59 |         theUsual
    |         ^^^^^^^^
 }}}


 It seems that it is floating `theUsual` into one of the blocks, causing
 the phantom type to infer as local to one of the blocks, and only allows
 it to be used in one block at a time. Switching to `do` instead of `mdo`
 fixes the problem (though it also forbids circular references, which I
 need).

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


More information about the ghc-tickets mailing list