
2012/1/8 Greg Weber
2012/1/8 Gábor Lehel
Thank you. I have a few questions/comments.
"The module/record ambiguity is dealt with in Frege by preferring modules and requiring a module prefix for the record if there is ambiguity."
I think I see why they do it this way (otherwise you can't refer to a module if a record by the same name is in scope), but on the other hand it would seem intuitive to me to choose the more specific thing, and a record feels more specific than a module. Maybe you could go that way and just not give your qualified imports the same name as a record? (Unqualified imports are in practice going to be hierarchical, and no one's in the habit of typing those out to disambiguate things, so I don't think it really matters if qualified records shadow them.)
In the case where a Record has the same name as its containing module it would be more specific than a module, and preferring it makes sense. I think doing this inside the module makes sense, as one shouldn't need to refer to the containing module's name. We should think more about the case where module & records are imported.
"Expressions of the form x.n: first infer the type of x. If this is just an unbound type variable (i.e. the type is unknown yet), then check if n is an overloaded name (i.e. a class operation). [...] Under no circumstances, however, will the notation x.n contribute in any way in inferring the type of x, except for the case when n is a class operation, where an appropriate class constraint is generated."
Is this just a simple translation from x.n to n x? What's the rationale for allowing the x.n syntax for, in addition to record fields, class methods specifically, but no other functions?
It is a simple translation from x.n to T.n x The key point being the function is only accessible through the record's namespace. The dot is only being used to tap into a namespace, and is not available for general function application.
I think my question and your answer are walking past each other here. Let me rephrase. The wiki page implies that in addition to using the dot to tap into a namespace, you can also use it for general function application in the specific case where the function is a class method ("appropriate class constraint is generated" etc etc). I don't understand why. Or am I misunderstanding?
Later on you write that the names of record fields are only accessible from the record's namespace and via record syntax, but not from the global scope. For Haskell I think it would make sense to reverse this decision. On the one hand, it would keep backwards compatibility; on the other hand, Haskell code is already written to avoid name clashes between record fields, so it wouldn't introduce new problems. Large gain, little pain. You could use the global-namespace function as you do now, at the risk of ambiguity, or you could use the new record syntax and avoid it. (If you were to also allow x.n syntax for arbitrary functions, this could lead to ambiguity again... you could solve it by preferring a record field belonging to the inferred type over a function if both are available, but (at least in my current state of ignorance) I would prefer to just not allow x.n for anything other than record fields.)
Perhaps you can give some example code for what you have in mind - we do need to figure out the preferred technique for interacting with old-style records. Keep in mind that for new records the entire point is that they must be name-spaced. A module could certainly export top-level functions equivalent to how records work now (we could have a helper that generates those functions).
Let's say you have a record. data Record = Record { field :: String } In existing Haskell, you refer to the accessor function as 'field' and to the contents of the field as 'field r', where 'r' is a value of type Record. With your proposal, you refer to the accessor function as 'Record.field' and to the contents of the field as either 'Record.field r' or 'r.field'. The point is that I see no conflict or drawback in allowing all of these at the same time. Writing 'field' or 'field r' would work exactly as it already does, and be ambiguous if there is more than one record field with the same name in scope. In practice, existing code is already written to avoid this ambiguity so it would continue to work. Or you could write 'Record.field r' or 'r.field', which would work as the proposal describes and remove the ambiguity, and work even in the presence of multiple record fields with the same name in scope. The point is that I see what you gain by allowing record fields to be referred to in a namespaced way, but I don't see what you gain by not allowing them to be referred to in a non-namespaced way. In theory you wouldn't care because the non-namespaced way is inferior anyways, but in practice because all existing Haskell code does it that way, it's significant.
Later on:
"- the function that updates field x of data type T is T.{x=} - the function that sets field x in a T to 42 is T.{x=42} - If a::T then a.{x=} and a.{x=42} are valid"
I think this looks considerably ugly. Aren't there better alternatives? { T.x = }, { T.x = 42 }, { a.x = }, { a.x = 42 } maybe? (Does this conflict in some unfinesseable way with explicit layout contexts?)
I think this is one of those slightly different syntaxes that many people will have an initial bad reaction to, however once they use it they will like it just fine. The problem with what you are suggesting is that it would be verbose when updating multiple fields at once. But we should investigate if it is possible to have a syntax closer to the existing update syntax.
Good point.
"the function that changes field x of a T by applying some function to it is T.{x <-}"
Same comment on syntax applies. I believe this is a new feature? It would be welcome, albeit the overloading of <- is a bit worrisome (don't have better ideas at the moment, but I think there was a thread). I assume T.{x <- f}, a.{x <-}, and a.{x <- f} (whatever the syntax is) would also be valid, by analogy to the above?
Yes, new feature, so not necessary in the initial implementation. I personally think Haskell should drop the monadic curly brackets which nobody uses, but whatever syntax works is fine with me.
Re: Compatibility with existing records: based on (very) cursory inspection I don't see an obstacle to making it (near-)fully compatible - you would just be adding some new syntax, most significantly x.n. Backwards compatibility is a great advantage, so why not?
Generalizing the syntax to arbitrary TDNR: I think I'm opposed to this. The problem is that in existing Haskell the vast majority of expressions (with the notable (and imho unfortunate) exception of (>>=)) flow from right to left. Going the other way with record fields isn't a big problem because it's simple and doesn't even feel like function application so much as member-selection (like modules), but if you were to allow any function you would soon end up with lengthy chains of them which would clash nastily with the surrounding code. Having to jump back and forth and switch directions while reading is unpleasant. OO languages have this problem and I don't envy them for it. And in particular having "a . b" mean "first do b, then do a", but "a.b" mean "do b to a" would be confusing. (You'd already have this problem with global namespace record field selectors, but at least it's localized.)
I agree - I think a.b or A.b should always mean tapping into a namespace and not be generalized outside of that.
All of that said, maybe having TDNR with bad syntax is preferable to not having TDNR at all. Can't it be extended to the existing syntax (of function application)? Or least some better one, which is ideally right-to-left? I don't really know the technical details...
Generalized data-namespaces: Also think I'm opposed. This would import the problem from OO languages where functions written by the module (class) author get to have a distinguished syntax (be inside the namespace) over functions by anyone else (which don't).
Maybe you can show some example code? To me this is about controlling exports of namespaces, which is already possible - I think this is mostly a matter of convenience.
If I'm understanding correctly, you're suggesting we be able to write: data Data = Data Int where twice (Data d) = 2 * d thrice (Data d) = 3 * d ... and that if we write 'let x = Data 7 in x.thrice' it would evaluate to 21. I have two objections. The first is the same as with the TDNR proposal: you would have both code that looks like 'data.firstFunction.secondFunction.thirdFunction', as well as the existing 'thirdFunction $ secondFunction $ firstFunction data' and 'thirdFunction . secondFunction . firstFunction $ data', and if you have both of them in the same expression (which you will) it becomes unpleasant to read because you have to read them in opposite directions. The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Another thing that would be nice is lenses to solve the nested-record-update problem - at least the room to add them later. Most of the proposed syntax would be unaffected, but you'd need some syntax for the lens itself... I'm not sure what it might be. Would it be terrible to have T.x refer to a lens rather than a getter? (I don't know how you'd refer to the getter then, so probably yeah.) Or maybe { T.x }, building backwards from { T.x = }?
Another existing language very similar to Haskell whose record system might be worth evaluating is Disciple: http://disciple.ouroborus.net/. Unfortunately I couldn't find any specific page it seemed best to link to.
The syntax of DDC seems the same as this proposal. However, I could not find any specific information either.
The main things I remember being interesting about it are that it's based on lenses, and uses some kind of extensible projectors system to allow something similar to what you achieve with datatype-namespaces, namely 'virtual' record fields. But I haven't studied it in detail.
On Sun, Jan 8, 2012 at 2:40 AM, Greg Weber
wrote: I have updated the wiki - the entry level page [1] compares the different proposals and points to a more fleshed out explanation of the Frege proposal [2].
I think I now understand the differences between the existing proposals and am able to provide leadership to move this forward. Let me summarize the state of things: There is a debate over extensible records that we are putting off into the future. Instead we have 2 proposals to make things better right now: * an overloaded record fields proposal that still has implementation concerns * a name-spacing & simple type resolution proposal that is awaiting your critique
The Frege language originally had overloaded record fields but then moved to the latter system. The existing experience of the Frege language is very fortunate for us as we now have some experience to help inform our own decision.
Greg Weber
[1] http://hackage.haskell.org/trac/ghc/wiki/Records [2] http://hackage.haskell.org/trac/ghc/wiki/Records/NameSpacing
On Wed, Jan 4, 2012 at 7:54 AM, Greg Weber
wrote: The Frege author does not have a ghc mail list account but gave a more detailed explanation of how he goes about TDNR for records and how often it type checks without annotation in practice.
A more general explanation is here:
http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders...
He sent a specific response to Simon's mail list message, quoted below:
Simon Peyton-Jones is absolutely correct when he notes:
Well the most obvious issue is this. 3.2 says e.m = (T.m e) if the expression e has type t and the type constructor of t is T and there exists a function T.m But that innocent-looking statement begs the *entire* question! How do we know if "e has type t?
The way it is done in Frege is such that, if you have a function that uses or updates (nondestructively, of course) a "record" then at least the type constructor of that record has to be known. This is no different than doing it explicitly with case constructs, etc., just here you learn the types from the constructors you write in the patterns.
Hence, it is not so that one can write a function that updates field f to 42 for any record that contains a field f:
foo x = x.{f=42} -- type annotation required for foo or x
In practice this means you'll have to write a type annotation here and there. Often, the field access is not the only one that happens to some variable of record type, or the record is the result of another function application. In such cases, the type is known. I estimate that in 2/3 of all cases one does not need to write (T.e x) in sparsely type annotated code, despite the fact that the frege type checker has a left to right bias and does not yet attempt to find the type of x in the code that "follows" the x.e construct (after let unrolling etc.) I think one could do better and guarantee that, if the type of x is inferrable at all, then so will be x.e (Still, it must be more than just a type variable.)
On Sun, Jan 1, 2012 at 2:39 PM, Greg Weber
wrote: On Sat, Dec 31, 2011 at 3:28 PM, Simon Peyton-Jones
wrote: Frege has a detailed explanation of the semantics of its record implementation, and the language is *very* similar to Haskell. Lets just start by using Frege's document as the proposal. We can start a new wiki page as discussions are needed.
If it’s a serious proposal, it needs a page to specify the design. Currently all we have is a paragraph on http://hackage.haskell.org/trac/ghc/wiki/Records, under “Better name spacing”.
As previously stated on this thread, the Frege user manual is available here:
http://code.google.com/p/frege/downloads/detail?name=Language-202.pdf
see Sections 3.2 (primary expressions) and 4.2.1 (Algebraic Data type Declaration - Constructors with labeled fields)
To all those concerned about Records: look at the Frege implementation and poke holes in it.
Well the most obvious issue is this. 3.2 says
e.m = (T.m e) if the expression e has type t and the type constructor
of t is T and there exists a function T.m
But that innocent-looking statement begs the *entire* question! How do we know if “e has type t? This is the route ML takes for arithmetic operators: + means integer plus if the argument is of type Int, float plus if the argument is of type Float, and so on.
Haskell type classes were specifically designed to address this situation. And if you apply type classes to the record situation, I think you end up with
http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields
More specifically I think of this as TDNR, which instead of the focus of the wiki page of maintaining backwards compatibility and de-surgaring to polymorphic constraints. I had hoped that there were different ideas or at least more flexibility possible for the TDNR implementation.
Well, so maybe we can give up on that. Imagine Frege without the above abbreviation. The basic idea is that field names are rendered unique by pre-pending the module name. As I understand it, to record selection one would then be forced to write (T.m e), to select the ‘m’ field. That is the, qualification with T is compulsory. The trouble with this is that it’s *already* possible; simply define suitably named fields
data T = MkE { t_m :: Int, t_n :: Bool }
Here I have prefixed with a (lower case version of) the type name. So we don’t seem to be much further ahead.
Maybe one could make it optional if there is no ambiguity, much like Haskell’s existing qualified names. But there is considerable ambiguity about whether T.m means
m imported from module T
or
the m record selector of data type T
If there is ambiguity, we expect the T to be a module. So you would need to refer to Record T's module: OtherModule.T.n or T.T.n Alternatively these conflicts could be compilation errors. Either way programmers are expected to structure their programs to avoid conflicting names, no different then they do now.
Perhaps one could make it work out. But before we can talk about it we need to see a design. Which takes us back to the question of leadership.
I am trying to provide as much leadership on this issue as I am capable of. Your critique is very useful in that effort.
At this point the Frege proposal without TDNR seems to be a small step forward. We can now define records with clashing fields in the same module. However, without TDNR we don't have convenient access to those fields. I am contacting the Frege author to see if we can get any more insights on implementation details.
Simon
We only want critiques about
* achieving name-spacing right now
* implementing it in such a way that extensible records could be implemented in its place in the future, although we will not allow that discussion to hold up a records implementation now, just possibly modify things slightly.
Greg Weber
On Thu, Dec 29, 2011 at 2:00 PM, Simon Peyton-Jones
wrote: | The lack of response, I believe, is just a lack of anyone who | can cut through all the noise and come up with some | practical way to move forward in one of the many possible | directions.
You're right. But it is very telling that the vast majority of responses on
http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders... were not about the subject (leadership) but rather on suggesting yet more, incompletely-specified solutions to the original problem. My modest attempt to build a consensus by articulating the simplest solution I could think of, manifestly failed.
The trouble is that I just don't have the bandwidth (or, if I'm honest, the motivation) to drive this through to a conclusion. And if no one else does either, perhaps it isn't *that* important to anyone. That said, it clearly is *somewhat* important to a lot of people, so doing nothing isn't very satisfactory either.
Usually I feel I know how to move forward, but here I don't.
Simon
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Work is punishment for failing to procrastinate effectively.
-- Work is punishment for failing to procrastinate effectively.

2012/1/8 Gábor Lehel
2012/1/8 Greg Weber
: 2012/1/8 Gábor Lehel
Thank you. I have a few questions/comments.
"The module/record ambiguity is dealt with in Frege by preferring modules and requiring a module prefix for the record if there is ambiguity."
I think I see why they do it this way (otherwise you can't refer to a module if a record by the same name is in scope), but on the other hand it would seem intuitive to me to choose the more specific thing, and a record feels more specific than a module. Maybe you could go that way and just not give your qualified imports the same name as a record? (Unqualified imports are in practice going to be hierarchical, and no one's in the habit of typing those out to disambiguate things, so I don't think it really matters if qualified records shadow them.)
In the case where a Record has the same name as its containing module it would be more specific than a module, and preferring it makes sense. I think doing this inside the module makes sense, as one shouldn't need to refer to the containing module's name. We should think more about the case where module & records are imported.
"Expressions of the form x.n: first infer the type of x. If this is just an unbound type variable (i.e. the type is unknown yet), then check if n is an overloaded name (i.e. a class operation). [...] Under no circumstances, however, will the notation x.n contribute in any way in inferring the type of x, except for the case when n is a class operation, where an appropriate class constraint is generated."
Is this just a simple translation from x.n to n x? What's the rationale for allowing the x.n syntax for, in addition to record fields, class methods specifically, but no other functions?
It is a simple translation from x.n to T.n x The key point being the function is only accessible through the record's namespace. The dot is only being used to tap into a namespace, and is not available for general function application.
I think my question and your answer are walking past each other here. Let me rephrase. The wiki page implies that in addition to using the dot to tap into a namespace, you can also use it for general function application in the specific case where the function is a class method ("appropriate class constraint is generated" etc etc). I don't understand why. Or am I misunderstanding?
Later on you write that the names of record fields are only accessible from the record's namespace and via record syntax, but not from the global scope. For Haskell I think it would make sense to reverse this decision. On the one hand, it would keep backwards compatibility; on the other hand, Haskell code is already written to avoid name clashes between record fields, so it wouldn't introduce new problems. Large gain, little pain. You could use the global-namespace function as you do now, at the risk of ambiguity, or you could use the new record syntax and avoid it. (If you were to also allow x.n syntax for arbitrary functions, this could lead to ambiguity again... you could solve it by preferring a record field belonging to the inferred type over a function if both are available, but (at least in my current state of ignorance) I would prefer to just not allow x.n for anything other than record fields.)
Perhaps you can give some example code for what you have in mind - we do need to figure out the preferred technique for interacting with old-style records. Keep in mind that for new records the entire point is that they must be name-spaced. A module could certainly export top-level functions equivalent to how records work now (we could have a helper that generates those functions).
Let's say you have a record.
data Record = Record { field :: String }
In existing Haskell, you refer to the accessor function as 'field' and to the contents of the field as 'field r', where 'r' is a value of type Record. With your proposal, you refer to the accessor function as 'Record.field' and to the contents of the field as either 'Record.field r' or 'r.field'. The point is that I see no conflict or drawback in allowing all of these at the same time. Writing 'field' or 'field r' would work exactly as it already does, and be ambiguous if there is more than one record field with the same name in scope. In practice, existing code is already written to avoid this ambiguity so it would continue to work. Or you could write 'Record.field r' or 'r.field', which would work as the proposal describes and remove the ambiguity, and work even in the presence of multiple record fields with the same name in scope.
The point is that I see what you gain by allowing record fields to be referred to in a namespaced way, but I don't see what you gain by not allowing them to be referred to in a non-namespaced way. In theory you wouldn't care because the non-namespaced way is inferior anyways, but in practice because all existing Haskell code does it that way, it's significant.
Later on:
"- the function that updates field x of data type T is T.{x=} - the function that sets field x in a T to 42 is T.{x=42} - If a::T then a.{x=} and a.{x=42} are valid"
I think this looks considerably ugly. Aren't there better alternatives? { T.x = }, { T.x = 42 }, { a.x = }, { a.x = 42 } maybe? (Does this conflict in some unfinesseable way with explicit layout contexts?)
I think this is one of those slightly different syntaxes that many people will have an initial bad reaction to, however once they use it they will like it just fine. The problem with what you are suggesting is that it would be verbose when updating multiple fields at once. But we should investigate if it is possible to have a syntax closer to the existing update syntax.
Good point.
"the function that changes field x of a T by applying some function to it is T.{x <-}"
Same comment on syntax applies. I believe this is a new feature? It would be welcome, albeit the overloading of <- is a bit worrisome (don't have better ideas at the moment, but I think there was a thread). I assume T.{x <- f}, a.{x <-}, and a.{x <- f} (whatever the syntax is) would also be valid, by analogy to the above?
Yes, new feature, so not necessary in the initial implementation. I personally think Haskell should drop the monadic curly brackets which nobody uses, but whatever syntax works is fine with me.
Re: Compatibility with existing records: based on (very) cursory inspection I don't see an obstacle to making it (near-)fully compatible - you would just be adding some new syntax, most significantly x.n. Backwards compatibility is a great advantage, so why not?
Generalizing the syntax to arbitrary TDNR: I think I'm opposed to this. The problem is that in existing Haskell the vast majority of expressions (with the notable (and imho unfortunate) exception of (>>=)) flow from right to left. Going the other way with record fields isn't a big problem because it's simple and doesn't even feel like function application so much as member-selection (like modules), but if you were to allow any function you would soon end up with lengthy chains of them which would clash nastily with the surrounding code. Having to jump back and forth and switch directions while reading is unpleasant. OO languages have this problem and I don't envy them for it. And in particular having "a . b" mean "first do b, then do a", but "a.b" mean "do b to a" would be confusing. (You'd already have this problem with global namespace record field selectors, but at least it's localized.)
I agree - I think a.b or A.b should always mean tapping into a namespace and not be generalized outside of that.
All of that said, maybe having TDNR with bad syntax is preferable to not having TDNR at all. Can't it be extended to the existing syntax (of function application)? Or least some better one, which is ideally right-to-left? I don't really know the technical details...
Generalized data-namespaces: Also think I'm opposed. This would import the problem from OO languages where functions written by the module (class) author get to have a distinguished syntax (be inside the namespace) over functions by anyone else (which don't).
Maybe you can show some example code? To me this is about controlling exports of namespaces, which is already possible - I think this is mostly a matter of convenience.
If I'm understanding correctly, you're suggesting we be able to write:
data Data = Data Int where twice (Data d) = 2 * d thrice (Data d) = 3 * d ...
and that if we write 'let x = Data 7 in x.thrice' it would evaluate to 21. I have two objections.
The first is the same as with the TDNR proposal: you would have both code that looks like 'data.firstFunction.secondFunction.thirdFunction', as well as the existing 'thirdFunction $ secondFunction $ firstFunction data' and 'thirdFunction . secondFunction . firstFunction $ data', and if you have both of them in the same expression (which you will) it becomes unpleasant to read because you have to read them in opposite directions.
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Another thing that would be nice is lenses to solve the nested-record-update problem - at least the room to add them later. Most of the proposed syntax would be unaffected, but you'd need some syntax for the lens itself... I'm not sure what it might be. Would it be terrible to have T.x refer to a lens rather than a getter? (I don't know how you'd refer to the getter then, so probably yeah.) Or maybe { T.x }, building backwards from { T.x = }?
Another existing language very similar to Haskell whose record system might be worth evaluating is Disciple: http://disciple.ouroborus.net/. Unfortunately I couldn't find any specific page it seemed best to link to.
The syntax of DDC seems the same as this proposal. However, I could not find any specific information either.
The main things I remember being interesting about it are that it's based on lenses, and uses some kind of extensible projectors system to allow something similar to what you achieve with datatype-namespaces, namely 'virtual' record fields. But I haven't studied it in detail.
Ah, I remember now where I saw a more thorough discussion: in his thesis[1]. Section 2.7 (page 115) and in particular 2.7.4 (119). It seems to be a very similar proposal to datatype-namespacing except it would address my second objection above and allow third-party code to add functions to the namespace as well. My first objection (the 'flow' of the code being in the opposite direction to all other code) still applies though. I couldn't find any discussion of lenses, except as pertaining to destructive update (which is another feature of Disciple). [1] http://www.cse.unsw.edu.au/~benl/papers/thesis/lippmeier-impure-world.pdf
On Sun, Jan 8, 2012 at 2:40 AM, Greg Weber
wrote: I have updated the wiki - the entry level page [1] compares the different proposals and points to a more fleshed out explanation of the Frege proposal [2].
I think I now understand the differences between the existing proposals and am able to provide leadership to move this forward. Let me summarize the state of things: There is a debate over extensible records that we are putting off into the future. Instead we have 2 proposals to make things better right now: * an overloaded record fields proposal that still has implementation concerns * a name-spacing & simple type resolution proposal that is awaiting your critique
The Frege language originally had overloaded record fields but then moved to the latter system. The existing experience of the Frege language is very fortunate for us as we now have some experience to help inform our own decision.
Greg Weber
[1] http://hackage.haskell.org/trac/ghc/wiki/Records [2] http://hackage.haskell.org/trac/ghc/wiki/Records/NameSpacing
On Wed, Jan 4, 2012 at 7:54 AM, Greg Weber
wrote: The Frege author does not have a ghc mail list account but gave a more detailed explanation of how he goes about TDNR for records and how often it type checks without annotation in practice.
A more general explanation is here:
http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders...
He sent a specific response to Simon's mail list message, quoted below:
Simon Peyton-Jones is absolutely correct when he notes:
Well the most obvious issue is this. 3.2 says e.m = (T.m e) if the expression e has type t and the type constructor of t is T and there exists a function T.m But that innocent-looking statement begs the *entire* question! How do we know if "e has type t?
The way it is done in Frege is such that, if you have a function that uses or updates (nondestructively, of course) a "record" then at least the type constructor of that record has to be known. This is no different than doing it explicitly with case constructs, etc., just here you learn the types from the constructors you write in the patterns.
Hence, it is not so that one can write a function that updates field f to 42 for any record that contains a field f:
foo x = x.{f=42} -- type annotation required for foo or x
In practice this means you'll have to write a type annotation here and there. Often, the field access is not the only one that happens to some variable of record type, or the record is the result of another function application. In such cases, the type is known. I estimate that in 2/3 of all cases one does not need to write (T.e x) in sparsely type annotated code, despite the fact that the frege type checker has a left to right bias and does not yet attempt to find the type of x in the code that "follows" the x.e construct (after let unrolling etc.) I think one could do better and guarantee that, if the type of x is inferrable at all, then so will be x.e (Still, it must be more than just a type variable.)
On Sun, Jan 1, 2012 at 2:39 PM, Greg Weber
wrote: On Sat, Dec 31, 2011 at 3:28 PM, Simon Peyton-Jones
wrote: > > Frege has a detailed explanation of the semantics of its record > implementation, and the language is *very* similar to Haskell. Lets > just > start by using Frege's document as the proposal. We can start a new > wiki > page as discussions are needed. > > > > If it’s a serious proposal, it needs a page to specify the design. > Currently all we have is a paragraph on > http://hackage.haskell.org/trac/ghc/wiki/Records, under “Better name > spacing”. > > > > As previously stated on this thread, the Frege user manual is > available > here: > > http://code.google.com/p/frege/downloads/detail?name=Language-202.pdf > > see Sections 3.2 (primary expressions) and 4.2.1 (Algebraic Data type > Declaration - Constructors with labeled fields) > > > > To all those concerned about Records: look at the Frege > implementation > and poke holes in it. > > > > Well the most obvious issue is this. 3.2 says > > e.m = (T.m e) if the expression e has type t and the type constructor > > of t is T and there exists a function T.m > > But that innocent-looking statement begs the *entire* question! How > do > we know if “e has type t? This is the route ML takes for arithmetic > operators: + means integer plus if the argument is of type Int, float > plus > if the argument is of type Float, and so on. > > > > Haskell type classes were specifically designed to address this > situation. And if you apply type classes to the record situation, I > think > you end up with > > > http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields More specifically I think of this as TDNR, which instead of the focus of the wiki page of maintaining backwards compatibility and de-surgaring to polymorphic constraints. I had hoped that there were different ideas or at least more flexibility possible for the TDNR implementation.
> > > > Well, so maybe we can give up on that. Imagine Frege without the > above > abbreviation. The basic idea is that field names are rendered unique > by > pre-pending the module name. As I understand it, to record selection > one > would then be forced to write (T.m e), to select the ‘m’ field. That > is > the, qualification with T is compulsory. The trouble with this is > that > it’s *already* possible; simply define suitably named fields > > data T = MkE { t_m :: Int, t_n :: Bool } > > Here I have prefixed with a (lower case version of) the type name. > So > we don’t seem to be much further ahead. > > > > Maybe one could make it optional if there is no ambiguity, much like > Haskell’s existing qualified names. But there is considerable > ambiguity > about whether T.m means > > m imported from module T > > or > > the m record selector of data type T
If there is ambiguity, we expect the T to be a module. So you would need to refer to Record T's module: OtherModule.T.n or T.T.n Alternatively these conflicts could be compilation errors. Either way programmers are expected to structure their programs to avoid conflicting names, no different then they do now.
> > > Perhaps one could make it work out. But before we can talk about it > we > need to see a design. Which takes us back to the question of > leadership. > >
I am trying to provide as much leadership on this issue as I am capable of. Your critique is very useful in that effort.
At this point the Frege proposal without TDNR seems to be a small step forward. We can now define records with clashing fields in the same module. However, without TDNR we don't have convenient access to those fields. I am contacting the Frege author to see if we can get any more insights on implementation details.
> > Simon > > > > > > We only want critiques about > > * achieving name-spacing right now > > * implementing it in such a way that extensible records could be > implemented in its place in the future, although we will not allow > that > discussion to hold up a records implementation now, just possibly > modify > things slightly. > > > > Greg Weber > > > > On Thu, Dec 29, 2011 at 2:00 PM, Simon Peyton-Jones >
wrote: > > | The lack of response, I believe, is just a lack of anyone who > | can cut through all the noise and come up with some > | practical way to move forward in one of the many possible > | directions. > > You're right. But it is very telling that the vast majority of > responses on > > > http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders... > were not about the subject (leadership) but rather on suggesting yet > more, incompletely-specified solutions to the original problem. My > modest > attempt to build a consensus by articulating the simplest solution I > could > think of, manifestly failed. > > The trouble is that I just don't have the bandwidth (or, if I'm > honest, > the motivation) to drive this through to a conclusion. And if no one > else > does either, perhaps it isn't *that* important to anyone. That said, > it > clearly is *somewhat* important to a lot of people, so doing nothing > isn't > very satisfactory either. > > Usually I feel I know how to move forward, but here I don't. > > Simon > > _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Work is punishment for failing to procrastinate effectively.
-- Work is punishment for failing to procrastinate effectively.
-- Work is punishment for failing to procrastinate effectively.

2012/1/8 Gábor Lehel
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Please allow me to clarify as far as Frege is concerned. In Frege, this is not so, because implementations of class functions in an instance will be linked back to the instantiated types name space. Hence one could do the following: module RExtension where import original.M(R) -- access the R record defined in module original.M class Rextension1 r where firstNewFunction :: ..... secondNewFunction :: ..... instance Rextension1 R where -- implementation for new functions And now, in another module one could import RExtension() -- equivalent to qualified import in Haskell and, voilá, the new functions are accessible (only) through R -- Mit freundlichen Grüßen Ingo

2012/1/8 Ingo Wechsung
2012/1/8 Gábor Lehel
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Please allow me to clarify as far as Frege is concerned. In Frege, this is not so, because implementations of class functions in an instance will be linked back to the instantiated types name space. Hence one could do the following:
module RExtension where
import original.M(R) -- access the R record defined in module original.M
class Rextension1 r where firstNewFunction :: ..... secondNewFunction :: .....
instance Rextension1 R where -- implementation for new functions
And now, in another module one could
import RExtension() -- equivalent to qualified import in Haskell
and, voilá, the new functions are accessible (only) through R
Ah, I see. And that answers my other question as well about why you would special case class methods like this. Thanks. I think I prefer Disciple's approach of introducing a new keyword alongside 'class' to distinguish 'virtual record fields' (which get put in the namespace) from any old class methods (which don't). Otherwise the two ideas seem very similar. (While at the same time I still dislike the wrong-direction aspect of both.) It strikes me that this kind of 'virtual record fields' (or 'projectors') thing is trying to tackle a very similar problem space as views and view patterns do. Namely, a kind of implementation hiding, eliding the difference between information which happens to represented by actual physical data and information which is merely calculated from that data. So you could in theory later change the representation (which fields are stored and which are calculated) without the client code noticing. There also seems to be an analogy between view patterns and simple function-based virtual-record-fields on the one hand, which are read-only, and full views and a theoretical lens-based virtual-record-fields on the other hand, which you can also use for update. (I don't see any way around having to write both the access and update function manually though, unless someone figures out how to invert functions in the general case). I don't have any follow-on thoughts from this at the moment but it seemed interesting to note.
-- Mit freundlichen Grüßen Ingo
-- Work is punishment for failing to procrastinate effectively.

2012/1/8 Gábor Lehel
2012/1/8 Ingo Wechsung
: 2012/1/8 Gábor Lehel
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Please allow me to clarify as far as Frege is concerned. In Frege, this is not so, because implementations of class functions in
an
instance will be linked back to the instantiated types name space. Hence one could do the following:
module RExtension where
import original.M(R) -- access the R record defined in module original.M
class Rextension1 r where firstNewFunction :: ..... secondNewFunction :: .....
instance Rextension1 R where -- implementation for new functions
And now, in another module one could
import RExtension() -- equivalent to qualified import in Haskell
and, voilá, the new functions are accessible (only) through R
Ah, I see. And that answers my other question as well about why you would special case class methods like this. Thanks. I think I prefer Disciple's approach of introducing a new keyword alongside 'class' to distinguish 'virtual record fields' (which get put in the namespace) from any old class methods (which don't). Otherwise the two ideas seem very similar. (While at the same time I still dislike the wrong-direction aspect of both.)
Yes, I can see your point here. OTOH, with the x.y.z notation the number of parentheses needed can be reduced drastically at times. In the end it's maybe a matter of taste. Frege started as a pure hobby project (inspired by Simon Peyton-Jones' paper "Practical type inference for higher ranked types"), but later I thought it may be interesting for OO programmers (especially Java) because of the low entry cost (just download a JAR, stay on JVM, etc.), and hence some aspects are designed so as to make them feel at home. Ironically, it turned out that the most interest is in the FP camp, while feedback from the Java camp is almost 0. Never mind! -- Mit freundlichen Grüßen Ingo

2012/1/8 Gábor Lehel
Later on you write that the names of record fields are only accessible from the record's namespace and via record syntax, but not from the global scope. For Haskell I think it would make sense to reverse this decision. On the one hand, it would keep backwards compatibility; on the other hand, Haskell code is already written to avoid name clashes between record fields, so it wouldn't introduce new problems. Large gain, little pain. You could use the global-namespace function as you do now, at the risk of ambiguity, or you could use the new record syntax and avoid it. (If you were to also allow x.n syntax for arbitrary functions, this could lead to ambiguity again... you could solve it by preferring a record field belonging to the inferred type over a function if both are available, but (at least in my current state of ignorance) I would prefer to just not allow x.n for anything other than record fields.)
Perhaps you can give some example code for what you have in mind - we do need to figure out the preferred technique for interacting with old-style records. Keep in mind that for new records the entire point is that they must be name-spaced. A module could certainly export top-level functions equivalent to how records work now (we could have a helper that generates those functions).
Let's say you have a record.
data Record = Record { field :: String }
In existing Haskell, you refer to the accessor function as 'field' and to the contents of the field as 'field r', where 'r' is a value of type Record. With your proposal, you refer to the accessor function as 'Record.field' and to the contents of the field as either 'Record.field r' or 'r.field'. The point is that I see no conflict or drawback in allowing all of these at the same time. Writing 'field' or 'field r' would work exactly as it already does, and be ambiguous if there is more than one record field with the same name in scope. In practice, existing code is already written to avoid this ambiguity so it would continue to work. Or you could write 'Record.field r' or 'r.field', which would work as the proposal describes and remove the ambiguity, and work even in the presence of multiple record fields with the same name in scope.
The point is that I see what you gain by allowing record fields to be referred to in a namespaced way, but I don't see what you gain by not allowing them to be referred to in a non-namespaced way. In theory you wouldn't care because the non-namespaced way is inferior anyways, but in practice because all existing Haskell code does it that way, it's significant.
My motivation for this entire change is simply to be able to use two record with field members of the same name. This requires *not* generating top-level functions to access record fields. I don't know if there is a valid use case for the old top-level functions once switched over to the new record system (other than your stated personal preference). We could certainly have a pragma or something similar that generates top-level functions even if the new record system is in use.
All of that said, maybe having TDNR with bad syntax is preferable to not having TDNR at all. Can't it be extended to the existing syntax (of function application)? Or least some better one, which is ideally right-to-left? I don't really know the technical details...
Generalized data-namespaces: Also think I'm opposed. This would import the problem from OO languages where functions written by the module (class) author get to have a distinguished syntax (be inside the namespace) over functions by anyone else (which don't).
Maybe you can show some example code? To me this is about controlling exports of namespaces, which is already possible - I think this is
mostly a
matter of convenience.
If I'm understanding correctly, you're suggesting we be able to write:
data Data = Data Int where twice (Data d) = 2 * d thrice (Data d) = 3 * d ...
and that if we write 'let x = Data 7 in x.thrice' it would evaluate to 21. I have two objections.
The first is the same as with the TDNR proposal: you would have both code that looks like 'data.firstFunction.secondFunction.thirdFunction', as well as the existing 'thirdFunction $ secondFunction $ firstFunction data' and 'thirdFunction . secondFunction . firstFunction $ data', and if you have both of them in the same expression (which you will) it becomes unpleasant to read because you have to read them in opposite directions.
This would not be possible because the functions can only be accessed from the namespace - you could only use the dot (or T.firstFunction). It is possible as per your complaint below:
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.

2012/1/8 Greg Weber
2012/1/8 Gábor Lehel
Later on you write that the names of record fields are only accessible from the record's namespace and via record syntax, but not from the global scope. For Haskell I think it would make sense to reverse this decision. On the one hand, it would keep backwards compatibility; on the other hand, Haskell code is already written to avoid name clashes between record fields, so it wouldn't introduce new problems. Large gain, little pain. You could use the global-namespace function as you do now, at the risk of ambiguity, or you could use the new record syntax and avoid it. (If you were to also allow x.n syntax for arbitrary functions, this could lead to ambiguity again... you could solve it by preferring a record field belonging to the inferred type over a function if both are available, but (at least in my current state of ignorance) I would prefer to just not allow x.n for anything other than record fields.)
Perhaps you can give some example code for what you have in mind - we do need to figure out the preferred technique for interacting with old-style records. Keep in mind that for new records the entire point is that they must be name-spaced. A module could certainly export top-level functions equivalent to how records work now (we could have a helper that generates those functions).
Let's say you have a record.
data Record = Record { field :: String }
In existing Haskell, you refer to the accessor function as 'field' and to the contents of the field as 'field r', where 'r' is a value of type Record. With your proposal, you refer to the accessor function as 'Record.field' and to the contents of the field as either 'Record.field r' or 'r.field'. The point is that I see no conflict or drawback in allowing all of these at the same time. Writing 'field' or 'field r' would work exactly as it already does, and be ambiguous if there is more than one record field with the same name in scope. In practice, existing code is already written to avoid this ambiguity so it would continue to work. Or you could write 'Record.field r' or 'r.field', which would work as the proposal describes and remove the ambiguity, and work even in the presence of multiple record fields with the same name in scope.
The point is that I see what you gain by allowing record fields to be referred to in a namespaced way, but I don't see what you gain by not allowing them to be referred to in a non-namespaced way. In theory you wouldn't care because the non-namespaced way is inferior anyways, but in practice because all existing Haskell code does it that way, it's significant.
My motivation for this entire change is simply to be able to use two record with field members of the same name. This requires *not* generating top-level functions to access record fields. I don't know if there is a valid use case for the old top-level functions once switched over to the new record system (other than your stated personal preference). We could certainly have a pragma or something similar that generates top-level functions even if the new record system is in use.
Oh, in a sense you're right. If the top-level accessor functions are treated as if they were defined by the module containing the record, and there is more than one with the same name, the compiler would see it as multiple definitions and indeed report an error. On the other hand if they are treated as imported names (conceptually, implicitly imported from the namespace of the record, say), then the compiler would only report an error when you actually try to use the ambiguous name. I had been assuming the latter case without realizing it. It corresponds to what you have now if you have multiple records imported with overlapping field names. Again, exporting the field accessors to global scope and deferring any errors from ambiguity or overlap to the point of their use would not in any way interfere with the use of those same field accessors with the namespaced syntax. If you only use the namespaced syntax, it would work exactly as in your proposal: the top-level accessors are never used so no ambiguity errors are reported. If you only use the top-level syntax, then it works almost exactly as Haskell currently does (except you can define multiple records with overlapping field names in the same module as long as you don't use them, which I had not considered). The set of well-formed programs if you allow top-level access would be almost a superset of the set of well-formed programs if you don't. (The exception is that top-level field accessors would conflict with non-accessor plain old functions of the same name, whereas if they weren't visible outside of the record's namespace they wouldn't, but I don't feel like that's a huge concern.)
All of that said, maybe having TDNR with bad syntax is preferable to not having TDNR at all. Can't it be extended to the existing syntax (of function application)? Or least some better one, which is ideally right-to-left? I don't really know the technical details...
Generalized data-namespaces: Also think I'm opposed. This would import the problem from OO languages where functions written by the module (class) author get to have a distinguished syntax (be inside the namespace) over functions by anyone else (which don't).
Maybe you can show some example code? To me this is about controlling exports of namespaces, which is already possible - I think this is mostly a matter of convenience.
If I'm understanding correctly, you're suggesting we be able to write:
data Data = Data Int where twice (Data d) = 2 * d thrice (Data d) = 3 * d ...
and that if we write 'let x = Data 7 in x.thrice' it would evaluate to 21. I have two objections.
The first is the same as with the TDNR proposal: you would have both code that looks like 'data.firstFunction.secondFunction.thirdFunction', as well as the existing 'thirdFunction $ secondFunction $ firstFunction data' and 'thirdFunction . secondFunction . firstFunction $ data', and if you have both of them in the same expression (which you will) it becomes unpleasant to read because you have to read them in opposite directions.
This would not be possible because the functions can only be accessed from the namespace - you could only use the dot (or T.firstFunction). It is possible as per your complaint below:
Sorry, I was unclear here. The firstFunction, secondFunction, and thirdFunction in my examples are *not* referring to the very same firstFunction, secondFunction, and thirdFunction, they are all placeholders for arbitrary functions. My problem is that you could (and would have to, because the syntaxes aren't interchangeable) write things like this: foo . bar . (baz.quux.asdf) . wasd $ hjkl Now what's the right order for reading the functions in this expression? The correct answer is: hjkl wasd baz quux asdf bar foo or using numbers to denote their place: 7 6 3 4 5 2 1 If you had written the equivalent using existing Haskell syntax it would be: foo . bar . (asdf $ quux baz) . wasd $ hjkl and the right order for reading it is: hjkl wasd baz quux asdf bar foo or with numbers: 7 6 5 4 3 2 1 If you introduce heavy use of the a.b.c.d syntax you would frequenty have to jump around and switch directions while you read an expression. If you restrict it to only field accessors I think it would be limited and tolerable, my quarrel is with allowing arbitrary functions (whether by TDNR or data-namespacing) in which case you would likely as not end up with half of functions going one way and the other half going the other.
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
-- Work is punishment for failing to procrastinate effectively.

On 08/01/2012, Gábor Lehel
2012/1/8 Greg Weber
: 2012/1/8 Gábor Lehel
Thank you. I have a few questions/comments.
"The module/record ambiguity is dealt with in Frege by preferring modules and requiring a module prefix for the record if there is ambiguity."
I think I see why they do it this way (otherwise you can't refer to a module if a record by the same name is in scope), but on the other hand it would seem intuitive to me to choose the more specific thing, and a record feels more specific than a module. Maybe you could go that way and just not give your qualified imports the same name as a record? (Unqualified imports are in practice going to be hierarchical, and no one's in the habit of typing those out to disambiguate things, so I don't think it really matters if qualified records shadow them.)
In the case where a Record has the same name as its containing module it would be more specific than a module, and preferring it makes sense. I think doing this inside the module makes sense, as one shouldn't need to refer to the containing module's name. We should think more about the case where module & records are imported.
"Expressions of the form x.n: first infer the type of x. If this is just an unbound type variable (i.e. the type is unknown yet), then check if n is an overloaded name (i.e. a class operation). [...] Under no circumstances, however, will the notation x.n contribute in any way in inferring the type of x, except for the case when n is a class operation, where an appropriate class constraint is generated."
Is this just a simple translation from x.n to n x? What's the rationale for allowing the x.n syntax for, in addition to record fields, class methods specifically, but no other functions?
It is a simple translation from x.n to T.n x The key point being the function is only accessible through the record's namespace. The dot is only being used to tap into a namespace, and is not available for general function application.
I think my question and your answer are walking past each other here. Let me rephrase. The wiki page implies that in addition to using the dot to tap into a namespace, you can also use it for general function application in the specific case where the function is a class method ("appropriate class constraint is generated" etc etc). I don't understand why. Or am I misunderstanding?
Later on you write that the names of record fields are only accessible from the record's namespace and via record syntax, but not from the global scope. For Haskell I think it would make sense to reverse this decision. On the one hand, it would keep backwards compatibility; on the other hand, Haskell code is already written to avoid name clashes between record fields, so it wouldn't introduce new problems. Large gain, little pain. You could use the global-namespace function as you do now, at the risk of ambiguity, or you could use the new record syntax and avoid it. (If you were to also allow x.n syntax for arbitrary functions, this could lead to ambiguity again... you could solve it by preferring a record field belonging to the inferred type over a function if both are available, but (at least in my current state of ignorance) I would prefer to just not allow x.n for anything other than record fields.)
Perhaps you can give some example code for what you have in mind - we do need to figure out the preferred technique for interacting with old-style records. Keep in mind that for new records the entire point is that they must be name-spaced. A module could certainly export top-level functions equivalent to how records work now (we could have a helper that generates those functions).
Let's say you have a record.
data Record = Record { field :: String }
In existing Haskell, you refer to the accessor function as 'field' and to the contents of the field as 'field r', where 'r' is a value of type Record. With your proposal, you refer to the accessor function as 'Record.field' and to the contents of the field as either 'Record.field r' or 'r.field'. The point is that I see no conflict or drawback in allowing all of these at the same time. Writing 'field' or 'field r' would work exactly as it already does, and be ambiguous if there is more than one record field with the same name in scope. In practice, existing code is already written to avoid this ambiguity so it would continue to work. Or you could write 'Record.field r' or 'r.field', which would work as the proposal describes and remove the ambiguity, and work even in the presence of multiple record fields with the same name in scope.
The point is that I see what you gain by allowing record fields to be referred to in a namespaced way, but I don't see what you gain by not allowing them to be referred to in a non-namespaced way. In theory you wouldn't care because the non-namespaced way is inferior anyways, but in practice because all existing Haskell code does it that way, it's significant.
Later on:
"- the function that updates field x of data type T is T.{x=} - the function that sets field x in a T to 42 is T.{x=42} - If a::T then a.{x=} and a.{x=42} are valid"
I think this looks considerably ugly. Aren't there better alternatives? { T.x = }, { T.x = 42 }, { a.x = }, { a.x = 42 } maybe? (Does this conflict in some unfinesseable way with explicit layout contexts?)
I think this is one of those slightly different syntaxes that many people will have an initial bad reaction to, however once they use it they will like it just fine. The problem with what you are suggesting is that it would be verbose when updating multiple fields at once. But we should investigate if it is possible to have a syntax closer to the existing update syntax.
Good point.
Perhaps we could use let-syntax, thus: let { r.x = x'; r.y = y'; r.z = z'; } in r If we allow tuples of selectors, thus: r.(x, y, z) = (r.x, r.y, r.z) then one can simply write let r.(x, y, z) = (x', y', z') in r
"the function that changes field x of a T by applying some function to it is T.{x <-}"
Same comment on syntax applies. I believe this is a new feature? It would be welcome, albeit the overloading of <- is a bit worrisome (don't have better ideas at the moment, but I think there was a thread). I assume T.{x <- f}, a.{x <-}, and a.{x <- f} (whatever the syntax is) would also be valid, by analogy to the above?
Yes, new feature, so not necessary in the initial implementation. I personally think Haskell should drop the monadic curly brackets which nobody uses, but whatever syntax works is fine with me.
Re: Compatibility with existing records: based on (very) cursory inspection I don't see an obstacle to making it (near-)fully compatible - you would just be adding some new syntax, most significantly x.n. Backwards compatibility is a great advantage, so why not?
Generalizing the syntax to arbitrary TDNR: I think I'm opposed to this. The problem is that in existing Haskell the vast majority of expressions (with the notable (and imho unfortunate) exception of (>>=)) flow from right to left. Going the other way with record fields isn't a big problem because it's simple and doesn't even feel like function application so much as member-selection (like modules), but if you were to allow any function you would soon end up with lengthy chains of them which would clash nastily with the surrounding code. Having to jump back and forth and switch directions while reading is unpleasant. OO languages have this problem and I don't envy them for it. And in particular having "a . b" mean "first do b, then do a", but "a.b" mean "do b to a" would be confusing. (You'd already have this problem with global namespace record field selectors, but at least it's localized.)
I agree - I think a.b or A.b should always mean tapping into a namespace and not be generalized outside of that.
All of that said, maybe having TDNR with bad syntax is preferable to not having TDNR at all. Can't it be extended to the existing syntax (of function application)? Or least some better one, which is ideally right-to-left? I don't really know the technical details...
Generalized data-namespaces: Also think I'm opposed. This would import the problem from OO languages where functions written by the module (class) author get to have a distinguished syntax (be inside the namespace) over functions by anyone else (which don't).
Maybe you can show some example code? To me this is about controlling exports of namespaces, which is already possible - I think this is mostly a matter of convenience.
If I'm understanding correctly, you're suggesting we be able to write:
data Data = Data Int where twice (Data d) = 2 * d thrice (Data d) = 3 * d ...
and that if we write 'let x = Data 7 in x.thrice' it would evaluate to 21. I have two objections.
The first is the same as with the TDNR proposal: you would have both code that looks like 'data.firstFunction.secondFunction.thirdFunction', as well as the existing 'thirdFunction $ secondFunction $ firstFunction data' and 'thirdFunction . secondFunction . firstFunction $ data', and if you have both of them in the same expression (which you will) it becomes unpleasant to read because you have to read them in opposite directions.
The second is that only the author of the datatype could put functions into its namespace; the 'data.foo' notation would only be available for functions written by the datatype's author, while for every other function you would have to use 'foo data'. I dislike this special treatment in OO languages and I dislike it here.
Another thing that would be nice is lenses to solve the nested-record-update problem - at least the room to add them later. Most of the proposed syntax would be unaffected, but you'd need some syntax for the lens itself... I'm not sure what it might be. Would it be terrible to have T.x refer to a lens rather than a getter? (I don't know how you'd refer to the getter then, so probably yeah.) Or maybe { T.x }, building backwards from { T.x = }?
Another existing language very similar to Haskell whose record system might be worth evaluating is Disciple: http://disciple.ouroborus.net/. Unfortunately I couldn't find any specific page it seemed best to link to.
The syntax of DDC seems the same as this proposal. However, I could not find any specific information either.
The main things I remember being interesting about it are that it's based on lenses, and uses some kind of extensible projectors system to allow something similar to what you achieve with datatype-namespaces, namely 'virtual' record fields. But I haven't studied it in detail.
On Sun, Jan 8, 2012 at 2:40 AM, Greg Weber
wrote: I have updated the wiki - the entry level page [1] compares the different proposals and points to a more fleshed out explanation of the Frege proposal [2].
I think I now understand the differences between the existing proposals and am able to provide leadership to move this forward. Let me summarize the state of things: There is a debate over extensible records that we are putting off into the future. Instead we have 2 proposals to make things better right now: * an overloaded record fields proposal that still has implementation concerns * a name-spacing & simple type resolution proposal that is awaiting your critique
The Frege language originally had overloaded record fields but then moved to the latter system. The existing experience of the Frege language is very fortunate for us as we now have some experience to help inform our own decision.
Greg Weber
[1] http://hackage.haskell.org/trac/ghc/wiki/Records [2] http://hackage.haskell.org/trac/ghc/wiki/Records/NameSpacing
On Wed, Jan 4, 2012 at 7:54 AM, Greg Weber
wrote: The Frege author does not have a ghc mail list account but gave a more detailed explanation of how he goes about TDNR for records and how often it type checks without annotation in practice.
A more general explanation is here:
http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders...
He sent a specific response to Simon's mail list message, quoted below:
Simon Peyton-Jones is absolutely correct when he notes:
Well the most obvious issue is this. 3.2 says e.m = (T.m e) if the expression e has type t and the type constructor of t is T and there exists a function T.m But that innocent-looking statement begs the *entire* question! How do we know if "e has type t?
The way it is done in Frege is such that, if you have a function that uses or updates (nondestructively, of course) a "record" then at least the type constructor of that record has to be known. This is no different than doing it explicitly with case constructs, etc., just here you learn the types from the constructors you write in the patterns.
Hence, it is not so that one can write a function that updates field f to 42 for any record that contains a field f:
foo x = x.{f=42} -- type annotation required for foo or x
In practice this means you'll have to write a type annotation here and there. Often, the field access is not the only one that happens to some variable of record type, or the record is the result of another function application. In such cases, the type is known. I estimate that in 2/3 of all cases one does not need to write (T.e x) in sparsely type annotated code, despite the fact that the frege type checker has a left to right bias and does not yet attempt to find the type of x in the code that "follows" the x.e construct (after let unrolling etc.) I think one could do better and guarantee that, if the type of x is inferrable at all, then so will be x.e (Still, it must be more than just a type variable.)
On Sun, Jan 1, 2012 at 2:39 PM, Greg Weber
wrote: On Sat, Dec 31, 2011 at 3:28 PM, Simon Peyton-Jones
wrote: > > Frege has a detailed explanation of the semantics of its record > implementation, and the language is *very* similar to Haskell. Lets > just > start by using Frege's document as the proposal. We can start a new > wiki > page as discussions are needed. > > > > If it’s a serious proposal, it needs a page to specify the design. > Currently all we have is a paragraph on > http://hackage.haskell.org/trac/ghc/wiki/Records, under “Better name > spacing”. > > > > As previously stated on this thread, the Frege user manual is > available > here: > > http://code.google.com/p/frege/downloads/detail?name=Language-202.pdf > > see Sections 3.2 (primary expressions) and 4.2.1 (Algebraic Data > type > Declaration - Constructors with labeled fields) > > > > To all those concerned about Records: look at the Frege > implementation > and poke holes in it. > > > > Well the most obvious issue is this. 3.2 says > > e.m = (T.m e) if the expression e has type t and the type > constructor > > of t is T and there exists a function T.m > > But that innocent-looking statement begs the *entire* question! How > do > we know if “e has type t? This is the route ML takes for > arithmetic > operators: + means integer plus if the argument is of type Int, > float > plus > if the argument is of type Float, and so on. > > > > Haskell type classes were specifically designed to address this > situation. And if you apply type classes to the record situation, I > think > you end up with > > > http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields More specifically I think of this as TDNR, which instead of the focus of the wiki page of maintaining backwards compatibility and de-surgaring to polymorphic constraints. I had hoped that there were different ideas or at least more flexibility possible for the TDNR implementation.
> > > > Well, so maybe we can give up on that. Imagine Frege without the > above > abbreviation. The basic idea is that field names are rendered > unique > by > pre-pending the module name. As I understand it, to record > selection > one > would then be forced to write (T.m e), to select the ‘m’ field. > That > is > the, qualification with T is compulsory. The trouble with this is > that > it’s *already* possible; simply define suitably named fields > > data T = MkE { t_m :: Int, t_n :: Bool } > > Here I have prefixed with a (lower case version of) the type name. > So > we don’t seem to be much further ahead. > > > > Maybe one could make it optional if there is no ambiguity, much like > Haskell’s existing qualified names. But there is considerable > ambiguity > about whether T.m means > > m imported from module T > > or > > the m record selector of data type T
If there is ambiguity, we expect the T to be a module. So you would need to refer to Record T's module: OtherModule.T.n or T.T.n Alternatively these conflicts could be compilation errors. Either way programmers are expected to structure their programs to avoid conflicting names, no different then they do now.
> > > Perhaps one could make it work out. But before we can talk about it > we > need to see a design. Which takes us back to the question of > leadership. > >
I am trying to provide as much leadership on this issue as I am capable of. Your critique is very useful in that effort.
At this point the Frege proposal without TDNR seems to be a small step forward. We can now define records with clashing fields in the same module. However, without TDNR we don't have convenient access to those fields. I am contacting the Frege author to see if we can get any more insights on implementation details.
> > Simon > > > > > > We only want critiques about > > * achieving name-spacing right now > > * implementing it in such a way that extensible records could be > implemented in its place in the future, although we will not allow > that > discussion to hold up a records implementation now, just possibly > modify > things slightly. > > > > Greg Weber > > > > On Thu, Dec 29, 2011 at 2:00 PM, Simon Peyton-Jones >
wrote: > > | The lack of response, I believe, is just a lack of anyone who > | can cut through all the noise and come up with some > | practical way to move forward in one of the many possible > | directions. > > You're right. But it is very telling that the vast majority of > responses on > > > http://www.reddit.com/r/haskell/comments/nph9l/records_stalled_again_leaders... > were not about the subject (leadership) but rather on suggesting yet > more, incompletely-specified solutions to the original problem. My > modest > attempt to build a consensus by articulating the simplest solution I > could > think of, manifestly failed. > > The trouble is that I just don't have the bandwidth (or, if I'm > honest, > the motivation) to drive this through to a conclusion. And if no one > else > does either, perhaps it isn't *that* important to anyone. That > said, > it > clearly is *somewhat* important to a lot of people, so doing nothing > isn't > very satisfactory either. > > Usually I feel I know how to move forward, but here I don't. > > Simon > > _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Work is punishment for failing to procrastinate effectively.
-- Work is punishment for failing to procrastinate effectively.
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
participants (4)
-
Greg Weber
-
Gábor Lehel
-
Ingo Wechsung
-
Matthew Farkas-Dyck