Posting etiquette, was Re: Records in Haskell

Malcolm Wallace malcolm.wallace at me.com
Thu Jan 19 11:14:30 CET 2012


Sorry to pick on your post in particular Matthew, but I have been seeing a lot of this on the Haskell lists lately.

I find it completely unreasonable for a reply to a very long post to quote the entire text, only to add a single line at the bottom (or worse, embedded in the middle somewhere).  In this case, there are 7 pages of quotation before your one-sentence contribution.  (That is on my laptop.  I dread to think how many pages it represents on a smartphone screen...)  Usually, if I need to scroll even to the second page-worth of quotation and have still not found any new text, I now just delete the post without reading it.

It is a failure to communicate well, on the part of the writer who values their own time more highly than that of their intended readers.  Even the much-maligned top-posting style, as forced upon Outlook users (and as I am doing right here), is preferable to the failure to trim, or to get to the point quickly.  My inbox has >1600 unread messages in it, and life is just too short.  So I offer this plea as a constructive social suggestion - if you want your ideas to reach their intended audience, don't annoy them before they have even seen what you want to say.

Regards,
    Malcolm


On 15 Jan 2012, at 20:33, Matthew Farkas-Dyck wrote:

> On 13/01/2012, Simon Peyton-Jones <simonpj at microsoft.com> wrote:
>> Thanks to Greg for leading the records debate.  I apologise that I
>> don't have enough bandwidth to make more than an occasional
>> contribution.  Greg's new wiki page, and the discussion so far has
>> clarified my thinking, and this message tries to express that new
>> clarity.  I put a conclusion at the end.
>> 
>> Simon
>> 
>> Overview
>> ~~~~~~~~
>> It has become clear that there are two elements to pretty much all the
>> proposals we have on the table.  Suppose we have two types, 'S' and 'T',
>> both with a field 'f', and you want to select field 'f' from a record 'r'.
>> Somehow you have to disambiguate which 'f' you mean.
>> 
>> (Plan A) Disambiguate using qualified names.  To select field f, say
>>        (S.f r) or (T.f r) respectively.
>> 
>> (Plan B) Disambiguate using types. This approach usually implies
>> dot-notation.
>>         If  (r::S), then (r.f) uses the 'f' from 'S', and similarly if
>> (r::T).
>> 
>> Note that
>> 
>> * The Frege-derived records proposal (FDR), uses both (A) and (B)
>>  http://hackage.haskell.org/trac/ghc/wiki/Records/NameSpacing
>> 
>> * The Simple Overloaded Record Fields (SORF) proposal uses only (B)
>>  http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields
>> 
>> * The Type Directed Name Resolution proposal (TDNR) uses only (B)
>> 
>> http://hackage.haskell.org/trac/haskell-prime/wiki/TypeDirectedNameResolution
>> 
>> I know of no proposal that advocates only (A).  It seems that we are agreed
>> that we must make use of types to disambigute common cases.
>> 
>> Complexities of (Plan B)
>> ~~~~~~~~~~~~~~~~~~~~~~~~
>> Proposal (Plan B) sounds innocent enough.  But I promise you, it isn't.
>> There has ben some mention of the "left-to-right" bias of Frege type
>> inference engine; indeed the wohle explanation of which programs are
>> accepted and which are rejected, inherently involves an understanding
>> of the type inference algorithm.  This is a Very Bad Thing when the
>> type inference algorithm gets complicated, and GHC's is certainly
>> complicated.
>> 
>> Here's an example:
>> 
>>   type family F a b
>>   data instance F Int [a] = Mk { f :: Int }
>> 
>>   g :: F Int b  -> ()
>>   h :: F a [Bool] -> ()
>> 
>>   k x = (g x, x.f, h x)
>> 
>> Consider type inference on k.  Initially we know nothing about the
>> type of x.
>> * From the application (g x) we learn that x's type has
>>   shape (F Int <something>).
>> * From the application (h x) we learn that x's type has
>>   shape (F <something else> [Bool])
>> * Hence x's type must be (F Int [Bool])
>> * And hence, using the data family we can see which field
>>   f is intended.
>> 
>> Notice that
>> a) Neither left to right nor right to left would suffice
>> b) There is significant interaction with type/data families
>>    (and I can give you more examples with classes and GADTs)
>> c) In passing we note that it is totally unclear how (Plan A)
>>    would deal with data families
>> 
>> This looks like a swamp.  In a simple Hindley-Milner typed language
>> you might get away with some informal heuristics, but Haskell is far
>> too complicated.
>> 
>> Fortunately we know exactly what to do; it is described in some detail
>> in our paper "Modular type inference with local assumptions"
>> http://www.haskell.org/haskellwiki/Simonpj/Talk:OutsideIn
>> 
>> The trick is to *defer* all these decisions by generating *type constraints*
>> and solving them later.  We express it like this:
>> 
>>   G, r:t1  |-  r.f : t2,  (Has t1 "f" t2)
>> 
>> This says that if r is in scope with type t1, then (r.f) has type t2,
>> plus the constraint (Has t1 "f" t2), which we read as saying
>> 
>>   Type t1 must have a field "f" of type t2
>> 
>> We gather up all the constraints and solve them.  In solving them
>> we may figure out t1 from some *other* constraint (to the left or
>> right, it's immaterial. That allow us to solve *this* constraint.
>> 
>> So it's all quite simple, uniform, and beautiful.  It'll fit right
>> into GHC's type-constraint solver.
>> 
>> But note what has happened: we have simply re-invented SORF.  So the
>> conclusion is this:
>> 
>>   the only sensible way to implement FDR is using SORF.
>> 
>> What about overloading?
>> ~~~~~~~~~~~~~~~~~~~~~~~
>> A feature of SORF is that you can write functions like this
>> 
>>   k :: Has r "f" Int => r -> Int
>>   k r = r.f + 1
>> 
>> Function 'k' works on any record that has a field 'f'.  This may be
>> cool, but it wasn't part of our original goal.  And indeed neither FDR
>> nor TDNR offer it.
>> 
>> But, the Has constraints MUST exist, in full glory, in the constraint
>> solver.  The only question is whether you can *abstract* over them.
>> Imagine having a Num class that you could not abstract over. So you
>> could write
>> 
>>   k1 x = x + x :: Float
>>   k2 x = x + x :: Integer
>>   k3 x = x + x :: Int
>> 
>> using the same '+' every time, which generates a Num constraint. The
>> type signature fixes the type to Float, Integer, Int respectively, and
>> tells you which '+' to use.  And that is exactly what ML does!
>> 
>> But Haskell doesn't.  The Coolest Thing about Haskell is that you get
>> to *abstract* over those Num constraints, so you can write
>> 
>>  k :: Num a => a -> a
>>  k x = x + x
>> 
>> and now it works over *any* Num type.
>> 
>> On reflection, it would be absurd not to do ths same thing for Has
>> constraints.  If we are forced to have Has constraints internally, it
>> woudl be criminal not to abstract over them.  And that is precisely
>> what SORF is.
>> 
>> 
>> Is (Plan A) worth it?
>> ~~~~~~~~~~~~~~~~
>> 
>> Once you have (Plan B), and SORF in full glory, plus of course the
>> existing ability to name fields T_f, S_f, if you want, I think it is
>> highly questionable whether we need the additional complexities of
>> (Plan A)?
>> 
>> And I do think (Plan A) has lots of additional complexities that we
>> have not begun to explore yet.  The data-family thing above is an
>> example, and I can think of some others.
>> 
>> But even if it was simple, we still have to ask: does *any* additional
>> complexity give enough payoff, if you already have SORF?  I suspect
>> not.
>> 
>> 
>> Extensions to SORF
>> ~~~~~~~~~~~~~~~~~~
>> Frege lets you add "virtual fields" to a record type, using an extra
>> RExtension mechanism that I do not yet understand.  But SORF lets you
>> do so with no additional mechanism.  See "Virtual record selectors"
>> on http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields
>> The point is that the existing type-class instance mechanisms do just
>> what we want.
>> 
>> 
>> Syntax
>> ~~~~~~
>> The debate on the mailing list has moved sharply towards discussing
>> lexical syntax.  I'm not going to engage in that discussion because
>> while it is useful to air opinions, it's very hard to get agreement.
>> But for the record:
>> 
>>  * I don't mind having Unicode alternatives, but there must be
>>    ASCII syntax too
>> 
>>  * I think we must use ordinary dot for field selection.
>> 
>>  * I think it's fine to insist on no spaces; we are already
>>    doing this for qualified names, as someone pointed out
>> 
>>  * I think we should not introduce new syntax unless we are
>>    absolutely forced into it.  Haskell's record update syntax
>>    isn't great, but we have it.
>> 
>> 
>> Conclusion
>> ~~~~~~~~~~
>> I am driven to the conclusion that SORF is the way to go.
>> - Every other proposal on the table requires SORF (either
>>   visibly or invisibly)
>> - When you have SORF, you don't really need anything else
>> 
>> The blocking issues are described on
>>  http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields
>> 
>>  a) "Representation hiding" (look for that heading)
>> 
>>  b) "Record update" (ditto), most especially for records whose
>>     fields have polymorphic types
> 
> I posted to the wiki a possible solution to (b):
> http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields#AlternativeProposal
> 
>> If we fix these we can move forward.
>> 
>> 
>> 
>> _______________________________________________
>> Glasgow-haskell-users mailing list
>> Glasgow-haskell-users at haskell.org
>> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>> 
> 
> _______________________________________________
> Glasgow-haskell-users mailing list
> Glasgow-haskell-users at haskell.org
> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users




More information about the Glasgow-haskell-users mailing list