Polymorphism over unboxed tuples

Simon Peyton Jones simonpj at microsoft.com
Fri Mar 24 13:14:38 UTC 2017


All true.  But perhaps the paper should articulate this thinking?

Simon

|  -----Original Message-----
|  From: ghc-devs [mailto:ghc-devs-bounces at haskell.org] On Behalf Of
|  Richard Eisenberg
|  Sent: 23 March 2017 16:19
|  To: Ryan Scott <ryan.gl.scott at gmail.com>
|  Cc: GHC developers <ghc-devs at haskell.org>
|  Subject: Re: Polymorphism over unboxed tuples
|  
|  This was a design choice in implementing, and one that is open for
|  revision (but not for 8.2).
|  
|  The key property is this:
|   (*) Two types with different representations must have different
|  kinds.
|  
|  Note that (*) does not stipulate what happens with two types with the
|  same representation, such as (# Int, (# Bool #) #) and (# Double, Char
|  #). We decided it was simpler to have unboxed tuples with the same
|  representation but different nestings to have different kinds. Part of
|  the complication with what’s proposed in the paper is that the kind of
|  unboxed tuple type constructors become more complicated. For example,
|  we would have
|  
|  (#,#) :: forall (r1 :: [UnaryRep]) (r2 :: [UnaryRep]). TYPE r1 -> TYPE
|  r2 -> TYPE (TupleRep (Concat ‘[r1, r2]))
|  
|  where Concat is a type family that does type-level list concatenation.
|  This would work. But would it have type inference consequences? (You
|  would be unable to infer the constituent kinds from the result kind.)
|  I doubt anyone would notice.
|  
|  The next problem comes when thinking about unboxed sums, though. To
|  implement unboxed sums (unmentioned in the paper) along similar lines,
|  you would need to include the quite-complicated algorithm for figuring
|  out the concrete representation of a sum from its types. For example,
|  (# (# Int, Int# #) | (# Word#, Int# #) #) takes up only 4 words in
|  memory: 1 each for the tag, the pointer to the Int, the Word#, and the
|  Int#. Note that the slot for the Int# is shared between the disjuncts!
|  We can’t share other slots because the GC properties for an Int are
|  different than for a Word#. But we also don’t take up 5 slots,
|  repeating the Int#. The algorithm to figure this out is thus somewhat
|  involved.
|  
|  If we wanted two unboxed sums with the same representations to have
|  the same kind, we would need to implement this algorithm in type
|  families. It’s doable, surely, but nothing I want to contemplate. And,
|  worse, it would expose this algorithm to users, who might start to
|  depend on it in their polymorphism. What if we decide to change it?
|  Then the type families change and users’ code breaks. Ich.
|  
|  Of course, we could use precise kinds for tuples (Concat isn’t hard
|  and isn’t likely to change) and imprecise kinds for sums. There’s
|  nothing wrong with such a system. But until a user appears (maybe
|  you!) asking for the precise kinds, it seems premature to commit
|  ourselves to that mode.
|  
|  Richard
|  
|  > On Mar 23, 2017, at 11:15 AM, Ryan Scott <ryan.gl.scott at gmail.com>
|  wrote:
|  >
|  > Section 4.2 of the paper Levity Polymorphism [1] makes a bold claim
|  > about polymorphism for unboxed tuples:
|  >
|  >  Note that this format respects the computational irrelevance of
|  > nesting of unboxed tuples. For example, these three types all have
|  the
|  > same kind, here written PFP for short:
|  >
|  >  type PFP = TYPE '[PtrRep, FloatRep, PtrRep]
|  >  (# Int, (# Float#, Bool #) #)        :: PFP
|  >  (# Int, Float#, Bool #)              :: PFP
|  >  (# (# Int, (# #) #), Float#, Bool #) :: PFP
|  >
|  > But in GHC, that isn't the case! Here's proof of it from a recent
|  GHCi session:
|  >
|  >  GHCi, version 8.3.20170322: http://www.haskell.org/ghc/  :? for
|  help
|  > λ> :set -XUnboxedTuples -XMagicHash  λ> import GHC.Exts  λ> :kind (#
|  > Int, (# Float#, Bool #) #)  (# Int, (# Float#, Bool #) #) :: TYPE
|  >                                     ('TupleRep '['LiftedRep,
|  'TupleRep
|  > '['FloatRep, 'LiftedRep]])  λ> :kind (# Int, Float#, Bool #)  (#
|  Int,
|  > Float#, Bool #) :: TYPE
|  >                               ('TupleRep '['LiftedRep, 'FloatRep,
|  > 'LiftedRep])
|  >  λ> :kind (# (# Int, (# #) #), Float#, Bool #)  (# (# Int, (# #) #),
|  > Float#, Bool #) :: TYPE
|  >                                            ('TupleRep
|  >                                               '['TupleRep
|  > '['LiftedRep, 'TupleRep '[]], 'FloatRep,
|  >                                                 'LiftedRep])
|  >
|  > As you can see, each of these different nestings of unboxed tuples
|  > yields different kinds, so they most certainly do *not* uphold the
|  > property claimed in the paper.
|  >
|  > Is this a bug? Or is there some reason that GHC implemented it
|  differently?
|  >
|  > Ryan S.
|  > -----
|  > [1]
|  > https://www.microsoft.com/en-us/research/wp-
|  content/uploads/2016/11/le
|  > vity-1.pdf _______________________________________________
|  > ghc-devs mailing list
|  > ghc-devs at haskell.org
|  > http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
|  
|  _______________________________________________
|  ghc-devs mailing list
|  ghc-devs at haskell.org
|  http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs


More information about the ghc-devs mailing list