Extensible records: Static duck typing
Hello haskell, the principle of duck typing used in dynamic OOP languages such as Ruby and Python, is simple: if some object supports Quack method, then it can be passed to any routine that expects an object of some Duck type this principle allows to build programs in quick and easy way: we just add to objects implementations of all the methods required: e = new Entry {label := "Hi", color := blue, getValue := getEntryValue, setValue := setEntryValue} e.display -- uses color/label properties e.saveToFile -- uses getValue property of course, drawback of duck typing that when we forgot to setup some field, this will be detected only at runtime - as usual for dynamic typing Haskell can provide benefits of both static and duck typing with type inference by means of extensible records. type of 'e' in this case will be {Entry | getValue::IO String, setValue::String->IO()}, i.e. Entry record type extended with getValue/setValue fields. serialization function may look like this: saveToFile x = writeToFile "data" (show x.getValue) and its implementation infers its type: saveToFile :: {...| getValue::(Show a) => a->IO() } -> IO () where {...| getValue::a} means "any record type which includes getValue field of type a". type of our 'e' conforms to this type signature, so `e` can be passed to saveToFile in safe manner and this is completely checked at compile time without any explicitly written type signatures this becomes even more important when we want to create objects belonging to more than one object hierarchy. by requiring that each possible property should be explicitly declared, we may ensure that properties used in different modules can't be mixed up: module A: property getValue module B: property getValue module C: import A; import B -- conflict: from where getValue should be imported? it may be interesting to compare extensible records with OOP and type classes approaches - it seems that e.r. is just like type class instances created "at the place" and linked to the concrete object rather than whole type so, implementation of extensible records in haskell compilers would allow to 1) give us simple, natural way to make bindings to various OOP libraries. i've seen bindings in gtk2hs/wxHaskell and now they use a lot of type hackery 2) compete with dynamic OOP languages in the areas of scripting, fast prototyping, web programming and even make possible to use the same techniques in larger apps, again increasing programmers productivity also, extensible records may be useful for merging haskell into jvm/.net world - as a way to provide haskell access to their OOP libs ps: there are many papers on adding extensible records to Haskell, in particular you can read http://research.microsoft.com/Users/simonpj/Papers/recpro.ps.gz -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com
On 05/02/2008, Bulat Ziganshin <bulat.ziganshin@gmail.com> wrote:
saveToFile x = writeToFile "data" (show x.getValue)
Heh, I had to read this a couple times to figure out that it wasn't just a blatant type error, and that (.) there doesn't mean function composition. :) On the matter of extensible records, I really like the semantics of Daan Leijen's proposal here: http://research.microsoft.com/users/daan/download/papers/scopedlabels.pdf However, the syntax could use some work. Using (.) as a record selector is out of the question. Personally, I think pt{x} for extracting the x field of pt seems not-so-unreasonable, and meshes well with the existing syntax for record updates. - Cale
On 05/02/2008, Cale Gibbard <cgibbard@gmail.com> wrote:
Personally, I think pt{x} for extracting the x field of pt seems not-so-unreasonable, and meshes well with the existing syntax for record updates.
I should clarify -- this is only if we can't somehow keep the existing function syntax for record extraction.
On Tue, Feb 05, 2008 at 05:57:24AM -0500, Cale Gibbard wrote:
On 05/02/2008, Bulat Ziganshin <bulat.ziganshin@gmail.com> wrote:
saveToFile x = writeToFile "data" (show x.getValue)
Heh, I had to read this a couple times to figure out that it wasn't just a blatant type error, and that (.) there doesn't mean function composition. :)
On the matter of extensible records, I really like the semantics of Daan Leijen's proposal here: http://research.microsoft.com/users/daan/download/papers/scopedlabels.pdf
However, the syntax could use some work. Using (.) as a record selector is out of the question. Personally, I think pt{x} for extracting the x field of pt seems not-so-unreasonable, and meshes well with the existing syntax for record updates.
The backwards compatable (and more clean conceptually IMHO) syntax I came up with for implementing the scoped labels proposal for jhc (sadly, not complete) was something like: new record (x = 3,y = 4) subtraction \r -> ( x = 3 | r - x) replacement \r -> (x := 3 | r) (equivalent to the above) type (x::Int,y::Char) degenerate cases: empty record (|) subtracting a label (| r - x) a record can always be determined by the presence of a '|' within parenthesis. note that these are unambigious because '=' and '|' are both reserved characters and cannot appear in parenthesis is this position otherwise. now when it came to record selection I was deciding between a couple. choice 1: use '.' as the current proposal suggests, but only when there is no space around it. choice 2: use ', declare that any identifier that _begins_ with ' always refers to a label selection function 'x point choice 3: use '#'. none are fully backwards compatable. I am still not sure which I like the best, ' has a lot of appeal to me as it is very simple to type and lightweight visually. note that instead of {} we use parens, the reason is that scoped labels have much more in common with tuples than the current labeld field mechanism so parens are a much more natural choice. you can think of tuples as 'anonymous positional data types' and records as 'anonymous labeled data types'. when thought about that way, parenthesis make a lot more sense. John -- John Meacham - ⑆repetae.net⑆john⑈
On 05/02/2008, John Meacham <john@repetae.net> wrote:
choice 2: use ', declare that any identifier that _begins_ with ' always refers to a label selection function
'x point
(snip)
none are fully backwards compatible. I am still not sure which I like the best, ' has a lot of appeal to me as it is very simple to type and lightweight visually.
I also like this idea. Retaining the ability to treat selection as a function easily is quite important, and this meets that criterion nicely. Also, in which case does this cause a program to break? It seems that you're only reinterpreting what would be unterminated character literals. Did you consider any options with regard to the syntax for variants as introduced in the paper? Perhaps something like (: and :) brackets could be used in place of the \langle and \rangle brackets used in the paper. Labels would still start with single quotes. We wouldn't need the decomposition syntax, just case, altered to agree with Haskell's existing syntax for case. Pattern matching against labels (whose names start with a single quote) unambiguously makes it clear that we're working with variants. - Cale
On Tue, Feb 05, 2008 at 08:01:07AM -0500, Cale Gibbard wrote:
I also like this idea. Retaining the ability to treat selection as a function easily is quite important, and this meets that criterion nicely. Also, in which case does this cause a program to break? It seems that you're only reinterpreting what would be unterminated character literals.
Ah, you are right. for some reason I was thinking we allowed identifiers to start with ', but yeah. this seems fully backwards compatable. while we are at it, we should allow ' in infix operators to. a *' b = almostMultiply a b John -- John Meacham - ⑆repetae.net⑆john⑈
Ouch. How would a human parse [apple'*'pear] If this doesn't immediately scan as [ (*') (apple') (pear) ] to you (it doesn't to me) then maybe allowing ' in infix operators may not be the best thing. John Meacham wrote:
On Tue, Feb 05, 2008 at 08:01:07AM -0500, Cale Gibbard wrote:
I also like this idea. Retaining the ability to treat selection as a function easily is quite important, and this meets that criterion nicely. Also, in which case does this cause a program to break? It seems that you're only reinterpreting what would be unterminated character literals.
Ah, you are right. for some reason I was thinking we allowed identifiers to start with ', but yeah. this seems fully backwards compatable. while we are at it, we should allow ' in infix operators to.
a *' b = almostMultiply a b
John
[hm. should this discussion move to -cafe?] On Feb 8, 2008, at 20:15 , Jonathan Cast wrote:
On 8 Feb 2008, at 4:43 PM, Dan Weston wrote:
Ouch. How would a human parse [apple'*'pear]
In this context, `parse error, tricky syntax'.
I kinda have that problem anyway given ' being permitted in identifiers at all. Given that I expect it now, the above isn't a whole lot worse (I've already parsed (token "apple'") when I hit the (token "*'"), the only question is whether the extension in use attaches the ' to the operator or to the following identifier. (Although I would assume the latter if I ran into it without prior knowledge, based on ' normally being a word-identifier character when it can't be a Char literal.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
On Fri, Feb 08, 2008 at 04:43:43PM -0800, Dan Weston wrote:
Ouch. How would a human parse [apple'*'pear]
If this doesn't immediately scan as [ (*') (apple') (pear) ] to you (it doesn't to me) then maybe allowing ' in infix operators may not be the best thing.
Oh, I was thinking they would only be allowed at the end of infix expressions, I'd even restrict them to the end of regular identifiers too actually if it didn't break backwards compatability. that would make everything unambiguous to parse. Id's like "Id's" are cute, but I do a double take every time I try to parse one with my brain :). John
John Meacham wrote:
On Tue, Feb 05, 2008 at 08:01:07AM -0500, Cale Gibbard wrote:
I also like this idea. Retaining the ability to treat selection as a function easily is quite important, and this meets that criterion nicely. Also, in which case does this cause a program to break? It seems that you're only reinterpreting what would be unterminated character literals.
Ah, you are right. for some reason I was thinking we allowed identifiers to start with ', but yeah. this seems fully backwards compatable. while we are at it, we should allow ' in infix operators to.
a *' b = almostMultiply a b
John
-- Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
-- John Meacham - ⑆repetae.net⑆john⑈
new record (x = 3,y = 4) subtraction \r -> ( x = 3 | r - x) replacement \r -> (x := 3 | r) (equivalent to the above) type (x::Int,y::Char)
degenerate cases: empty record (|) subtracting a label (| r - x)
a record can always be determined by the presence of a '|' within parenthesis.
One of the advantages of the systems with richer polymorphism and more predicates is that they need less syntax. It is possible (once you have solved the permutation/scoping problem) to use constructors as labels, and define all the basic operators on records as standard Haskell functions. With this approach you can even treat labels as "first-class citizens" and write polymorphic record zip: labelZip :: ({n :: a} `Disjoint` {m :: b}) => n -> m -> [a] -> [b] -> [{n :: a, m :: b}] labelZip n m = zipWith (\x y -> {n := x, m := y}) But no-one knows whether this extra expressive power has an unacceptable cost in terms of extra complexity, because no-one has implemented and used these systems seriously. Barney.
On Feb 5, 2008 4:24 AM, John Meacham <john@repetae.net> wrote:
now when it came to record selection I was deciding between a couple.
<...snip.../>
...declare that any identifier that _begins_ with ' always refers to a label selection function
'x point
Say we go with 'x and allow it to pick the x field out of records. All records. Then we have implicitly defined a function 'x that accepts things in the HasAnX class. This class is also implicitly defined -- and things are added to it implicitly, too, by giving them an x. So, in a way, this is cool -- it's like structs but way less verbose. On the other hand, it seems awfully like something that could be handled as a templating thing. Since we already have templates, couldn't we just add a few default ones to GHC and be done with it? -- _jsn
Everyone wants to add extensible records to Haskell. The problem is that, in a formally defined language like Haskell, we need to agree how they should behave, and there are too many conflicting ideas. I was involved recently in an attempt to try to sort out some of the alternatives (recorded here: http://hackage.haskell.org/trac/ghc/wiki/ExtensibleRecords) which collapsed because of argument over a fundamental question: Should {label := "Hi", color := blue} and {color := blue, label := "Hi"} have the same type? One of the main contributors felt that the answer was no (because it allows more different records to be represented, and makes implementation simpler), and that we should say so. I felt that most people would consider that the answer was yes, and that we shouldn't make such a fundamental design decision without some evidence about what is best in practice. The result was that our attempt to sort things out stopped. This sort of disagreement means that nothing gets done. After my experience with the wiki page, I don't believe anything will get done until one of the core ghc developers makes some arbitrary decisions and implements whatever they want to, which will then eventually become part of the standard by default. Barney.
On 05/02/2008, Barney Hilken <b.hilken@ntlworld.com> wrote:
Should {label := "Hi", color := blue} and {color := blue, label := "Hi"} have the same type?
The scoped labels paper has an interesting feature in this regard: labels with different names can be swapped at will, but labels having the same name (which is allowed) maintain their order. The types are equivalent in either case though. This is part of the general means by which the system avoids the need for lacks-predicates, and as a bonus, allows each field name of a record to act a bit like a stack. - Cale
On 05/02/2008, Cale Gibbard <cgibbard@gmail.com> wrote:
On 05/02/2008, Barney Hilken <b.hilken@ntlworld.com> wrote:
Should {label := "Hi", color := blue} and {color := blue, label := "Hi"} have the same type?
The scoped labels paper has an interesting feature in this regard: labels with different names can be swapped at will, but labels having the same name (which is allowed) maintain their order. The types are equivalent in either case though.
Er, sorry, no they're not. Two labels with the same name may have different types, and you can't swap them at the type level either, obviously.
The scoped labels paper has an interesting feature in this regard: labels with different names can be swapped at will, but labels having the same name (which is allowed) maintain their order.
- Cale
Yes, I know. The problem is that there are TOO MANY proposals, and they are all fundamentally incompatible. The scoped labels idea is interesting, but is it useful? No-one has written enough code with ANY of the proposals to say what their strengths and weaknesses are. Barney.
Everyone wants to add extensible records to Haskell.
well ... sure records are better than tuples ... but interfaces (uh, classes) are still better IMHO but anyway, is it possible to steal the design of C#'s anonymous types (classes)? if not, then why? (this might help to clarify what we want, or not). best regards, J.W.
On Feb 5, 2008 11:08 AM, Barney Hilken <b.hilken@ntlworld.com> wrote:
This sort of disagreement means that nothing gets done. After my experience with the wiki page, I don't believe anything will get done until one of the core ghc developers makes some arbitrary decisions and implements whatever they want to, which will then eventually become part of the standard by default.
This is the sort of situation where a "benign dictator" is needed. I have no strong feelings about which of all of these (all very good) proposals get implemented, but I do have a strong opinion that the lack of "proper" records is hurting Haskell quite a bit. Any of them will do, just get it in there! I'm assuming that Simon {PJ,M} et al. won't make an obviously terrible choice, and GHC seems to be the de facto standard anyway, so if they just implemented something in GHC that would be good enough for me, and a shoe-in for a future standard. On 05/02/2008, Cale Gibbard <cgibbard@gmail.com> wrote:
On 05/02/2008, Cale Gibbard <cgibbard@gmail.com> wrote:
Personally, I think pt{x} for extracting the x field of pt seems not-so-unreasonable, and meshes well with the existing syntax for record updates.
I should clarify -- this is only if we can't somehow keep the existing function syntax for record extraction.
Only if they get a separate namespace for each record, rather than overlapping, which would probably be confusing as they would *look* like functions, but they wouldn't really be function... That said, I like the "record{field}" syntax. It's sort of like array accessors in C style languages, but follows the "flavour" of the rest of the record syntax. I like the dot better, though, but I agree that it's too overloaded as it is. -- Sebastian Sylvan +44(0)7857-300802 UIN: 44640862
This sort of disagreement means that nothing gets done. After my experience with the wiki page, I don't believe anything will get done until one of the core ghc developers makes some arbitrary decisions and implements whatever they want to, which will then eventually become part of the standard by default. This is the sort of situation where a "benign dictator" is needed. I have no strong feelings about which of all of these (all very good) proposals get implemented, but I do have a strong opinion that the lack of "proper" records is hurting Haskell quite a bit. Any of them will do, just get it in there! I'm assuming that Simon {PJ,M} et al. won't make an obviously terrible choice, and GHC seems to be the de facto standard anyway, so if they just implemented something in GHC that would be good enough for me, and a shoe-in for a future standard. Since you are taking my name in vain, I had better respond! I wish I felt as confident of my good taste as you do. My last attempt to implement records (with Umut Acar, more or less the design in the "proposal for records" paper) involved rather significant changes to the type checker, and I was reluctant to commit to them without stronger evidence that it was a good design. I'm not so despondent about the Wiki page. It's already a good start. Don't give up too soon! You say that "lack of proper records is hurting Haskell". I think it'd help to give much more structure to that statement. "proper records" means different things to different people. After all Haskell already has somethng you can call "records" but they obviously aren't "proper" for you. So it might be interesting to do several things. 1. List the interested parties. The Wiki page doesn't say who's interested in this so it's hard to judge whether the "hurting" is a widely held opinion or not. 2. List the possible features that "records" might mean. For example: * Anonymous records as a type. So {x::Int, y::Bool} is a type. (In Haskell as it stands, records are always associated with a named data type. * Polymorphic field access. r.x accesses a field in any record with field x, not just one record type. * Polymorphic extension * Record concatenation * Are labels first-class? * etc Give examples of why each is useful. Simply writing down these features in a clear way would be a useful exercise. Probably some are "must have" for some people, but others might be optional. 3. Cross-tabulate, for each of the current proposals, say which features they have. 4. Reflect on how invasive each proposal would be, given the existence of type functions. Simon
On Feb 6, 2008 11:33 AM, Simon Peyton-Jones <simonpj@microsoft.com> wrote:
This sort of disagreement means that nothing gets done. After my experience with the wiki page, I don't believe anything will get done until one of the core ghc developers makes some arbitrary decisions and implements whatever they want to, which will then eventually become part of the standard by default.
This is the sort of situation where a "benign dictator" is needed. I have no strong feelings about which of all of these (all very good) proposals get implemented, but I do have a strong opinion that the lack of "proper" records is hurting Haskell quite a bit.
Any of them will do, just get it in there! I'm assuming that Simon {PJ,M} et al. won't make an obviously terrible choice, and GHC seems to be the de facto standard anyway, so if they just implemented something in GHC that would be good enough for me, and a shoe-in for a future standard.
Since you are taking my name in vain, I had better respond! I wish I felt as confident of my good taste as you do. My last attempt to implement records (with Umut Acar, more or less the design in the "proposal for records" paper) involved rather significant changes to the type checker, and I was reluctant to commit to them without stronger evidence that it was a good design.
I'm not so despondent about the Wiki page. It's already a good start. Don't give up too soon!
You say that "lack of proper records is hurting Haskell". I think it'd help to give much more structure to that statement. "proper records" means different things to different people. After all Haskell already has somethng you can call "records" but they obviously aren't "proper" for you.
So to clarify that statement. Honestly the number one problem I have with the current records system is that labels share the same namespace. This makes interfacing with any C library using structs quite painful. This is why I say that I don't really care which gets implemented. The current system is *painful* IMO, so anything which improves on it would be welcome (even if just puts the record accessors in a per-record namespace, where with syntactic sugar to avoid having to qualify it). Now, I do think that if we're going to remedy that situation, we might as well take the opportunity to make them lightweight (i.e. not require a data type), with polymorphic fields etc, but those all of those things are distant second priority to the one "showstopper" for the current records, IMO. Sebastian
So to clarify that statement. Honestly the number one problem I have with the current records system is that labels share the same namespace. This makes interfacing with any C library using structs quite painful. This is why I say that I don't really care which gets implemented. The current system is *painful* IMO, so anything which improves on it would be welcome (even if just puts the record accessors in a per-record namespace, where with syntactic sugar to avoid having to qualify it). You do know about: http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#di... don't you? S
On Feb 6, 2008 12:19 PM, Simon Peyton-Jones <simonpj@microsoft.com> wrote:
So to clarify that statement. Honestly the number one problem I have with the current records system is that labels share the same namespace. This makes interfacing with any C library using structs quite painful. This is why I say that I don't really care which gets implemented. The current system is *painful* IMO, so anything which improves on it would be welcome (even if just puts the record accessors in a per-record namespace, where with syntactic sugar to avoid having to qualify it).
You do know about: http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#di...
don't you?
I did not! This is great and takes care of my immediate concerns. I do still think that all that other stuff is worthwhile, particularly I like the low overhead of using tuples, and wouldn't mind if records were similarly convenient. -- Sebastian Sylvan +44(0)7857-300802 UIN: 44640862
2. List the possible features that “records” might mean. For example:
· Anonymous records as a type. So {x::Int, y::Bool} is a type. (In Haskell as it stands, records are always associated with a named data type.
· Polymorphic field access. r.x accesses a field in any record with field x, not just one record type.
· Polymorphic extension
· Record concatenation
· Are labels first-class?
· etc
Give examples of why each is useful. Simply writing down these features in a clear way would be a useful exercise. Probably some are “must have” for some people, but others might be optional.
This is what I was trying to do with the wiki page. I stopped because the only other contributor decided he could no longer contribute, and I felt I was talking to myself. If we want to be rational about the design, we need real examples to demonstrate what is genuinely useful, and I don't have that many of them. Barney.
Simon Peyton-Jones wrote:
Since you are taking my name in vain, I had better respond! I wish I felt as confident of my good taste as you do. My last attempt to implement records (with Umut Acar, more or less the design in the "proposal for records" paper) involved rather significant changes to the type checker, and I was reluctant to commit to them without stronger evidence that it was a good design.
What about just implementing the cheapest solution that still gets us most of the way? Yes, I mean yours: http://research.microsoft.com/~simonpj/Haskell/records.html "...It is a little less expressive than the earlier proposal, but (a) it is considerably simpler to implement, and (b) it is rather simpler to explain. In particular, we can translate source programs using records into System Fw, GHC's existing, strongly-typeed intermediate language. This is a major benefit, because it means that the existing transformations and optimisations in GHC's middle end can remain unchanged." I am suggesting that this proposal gets implemented in GHC. (1) Lack of records is biting me every day, literally. There is NOTHING that is missing from Haskell (the language) as much as (extensible) records. (2) You already thought about it. You think it is easier to implement than other, more ambitious, proposals. This means chances are great that we can ACTUALLY HAVE something in the not too distant future instead of debating the issue until hell freezes over. (3) If it is as cheap (to implement) as advertised then there is no great risk involved. If it turns out the missing features are a great show-stopper for some people (which I don't believe) then let them present their case afterwards, with good examples at hand. We can still decide to aim for a higher goal in the long term. (4) An implementation that can be evaluated opens the road to standardization in Haskell' (its motto being that only tried and implemented features go in). (5) Don't underestimate the psychological effect this would have. I am dead certain that many, many people would immediately jump on the train and use them in their code, limitations or not. I certainly would. The article lists some open questions. I want to chime in and propose that you decide them at your leisure. If in doubt, chose the solution that is easier to implement. If this doesn't help, post specific questions on the list and ask for opinions, then let majority of interested people decide. The important thing is to have something real to start experimenting with. Cheers Ben
What about just implementing the cheapest solution that still gets us most of the way?
(3) If it is as cheap (to implement) as advertised then there is no great risk involved. If it turns out the missing features are a great show-stopper for some people (which I don't believe) then let them present their case afterwards, with good examples at hand. We can still decide to aim for a higher goal in the long term.
If in doubt, chose the solution that is easier to implement.
Since this paper, there have been several proposals which can be 90% implemented as libraries, using either functional dependencies or associated types. These all have much more expressive type systems than the SPJ paper, yet need very little compiler support. The question is, which one (if any) should get this small but necessary support? Barney.
[I replied on @cafe but didn't get any response. Trying again here.] Barney Hilken wrote:
What about just implementing the cheapest solution that still gets us most of the way?
(3) If it is as cheap (to implement) as advertised then there is no great risk involved. If it turns out the missing features are a great show-stopper for some people (which I don't believe) then let them present their case afterwards, with good examples at hand. We can still decide to aim for a higher goal in the long term.
If in doubt, chose the solution that is easier to implement.
Since this paper, there have been several proposals which can be 90% implemented as libraries, using either functional dependencies or associated types. These all have much more expressive type systems than the SPJ paper, yet need very little compiler support. The question is, which one (if any) should get this small but necessary support?
Could you be more specific? Which proposals exactly do you mean and where can I read more about them? (I know about HList/OOHaskell which is ingenious, of course, but not even the authors propose that a new Record System for Haskell should be based on their library, compiler support or no.) Cheers Ben
Begin forwarded message:
From: Ben Franksen <ben.franksen@online.de> Date: 18 February 2008 21:32:29 GMT To: haskell@haskell.org Could you be more specific? Which proposals exactly do you mean and where can I read more about them?
Hlist is one of the ones | was thinking of. Two more are "poor man's records" a.k.a. Data.Record.hs, whose author certainly believes it should be the basis for the new system, and my own system, downloadable from http://homepage.ntlworld.com/b.hilken/files/Records.hs There is a discussion of the various possibilities on the wiki http://hackage.haskell.org/trac/ghc/wiki/ExtensibleRecords which you are encouraged to contribute to! Sorry about the lack of response on cafe, but I only read that when people say they are moving their discussion there. Barney.
Barney Hilken wrote:
From: Ben Franksen <ben.franksen@online.de> Date: 18 February 2008 21:32:29 GMT To: haskell@haskell.org Could you be more specific? Which proposals exactly do you mean and where can I read more about them?
Hlist is one of the ones | was thinking of. Two more are "poor man's records" a.k.a. Data.Record.hs, whose author certainly believes it should be the basis for the new system, and my own system, downloadable from http://homepage.ntlworld.com/b.hilken/files/Records.hs
There is a discussion of the various possibilities on the wiki http://hackage.haskell.org/trac/ghc/wiki/ExtensibleRecords which you are encouraged to contribute to!
Thanks, exactly the kind of pointer I had hoped for! I'll read up on the existing proposals.
Sorry about the lack of response on cafe, but I only read that when people say they are moving their discussion there.
No offense meant, you are right, I should have posted a redirection message here. Cheers Ben
Ben Franksen wrote:
Barney Hilken wrote:
From: Ben Franksen <ben.franksen@online.de> Date: 18 February 2008 21:32:29 GMT To: haskell@haskell.org Could you be more specific? Which proposals exactly do you mean and where can I read more about them?
Hlist is one of the ones | was thinking of. Two more are "poor man's records" a.k.a. Data.Record.hs, whose author certainly believes it should be the basis for the new system, and my own system, downloadable from http://homepage.ntlworld.com/b.hilken/files/Records.hs
There is a discussion of the various possibilities on the wiki http://hackage.haskell.org/trac/ghc/wiki/ExtensibleRecords which you are encouraged to contribute to!
Thanks, exactly the kind of pointer I had hoped for! I'll read up on the existing proposals.
My proposal how to proceed is to /eliminate/ candidates by the following criteria: (a) efficient field access (b) requires no or only minimal /additional/ extensions to compiler or language (c) light-weight syntax and semantics, at least for the simple non-polymorphic cases My rationale for these criteria goes like this: efficient access is necessary if we want to compete with the much simpler record systems in mainstream languages. If records are not as light-weight (syntactically as well as wrt run-time performance) as 'normal' Haskell data types, then people will be reluctant to use them, especially in library APIs. Finally, having to wait for highly experimental additional extensions to be available, tried, and tested, would only help to indefinitely post-pone the introduction of a usable record system. Now, these three criteria together immediately eliminate all the 'mostly library' proposals, including HeterogeneousCollections, Data.Record.hs, and TypeFamilies, since in all of them records are represented as nested pairs (more or less) and efficient field access relies on not yet available compiler optimizations i.e. 'partial evaluation of type class programs'. They also directly depend on advanced type system extensions that are either only partially implemented yet (type families, type sharing), or controversial (fundeps). Criterion (c) also eliminates proposals in which labels must be pre-declared; it is not completely clear to me which of the 'mostly library' proposals require how much compiler support to completely automate this. TREX seems to be generally agreed to be too complicated to implement and explain. What remains are Daan's ScopedLabels approach and SPJ's proposal which I mentioned earlier. Both are simple to explain, support light-weight syntax, and require only minimal or no additional (type system or other) extensions beyond what the proposal itself specifies. These are IMO the only really practical proposals. It is, however, unclear to me whether the ScopedLabels proposal can be implemented effciently (AFAIR, the issue is only briefly mentioned at the end of the paper). Just my 2 cents. Cheers Ben
My rationale for these criteria goes like this: efficient access is necessary if we want to compete with the much simpler record systems in mainstream languages. If records are not as light-weight (syntactically as well as wrt run-time performance) as 'normal' Haskell data types, then people will be reluctant to use them, especially in library APIs. Finally, having to wait for highly experimental additional extensions to be available, tried, and tested, would only help to indefinitely post- pone the introduction of a usable record system.
I totally disagree. The great strength of Haskell is that, whenever important design decisions have been made, the primary consideration has not been practicality, but generality and mathematical foundation. When the Haskell committee first started work, many people said lazy evaluation was an academic curiosity: mathematically right, but far too inefficient for real programs. When Haskell adopted type classes, people said they were far too heavy a machinery to solve the relatively simple problems of equality, show and numbers. In each case the more general, abstract approach has shown enormous advantages in the long term. I'm sure the same will be true of associated types, which are a lot more complex than functional dependencies, but also more general, and more mathematical. My criteria for choosing a record system would be: continue with the philosophy which has served Haskell so well up to now. In other words, choose the system which is most general and most mathematically sound; get some kind of implementation working so that we can get some experience with using it; then worry about efficiency later. Barney.
Barney Hilken wrote:
My rationale for these criteria goes like this: efficient access is necessary if we want to compete with the much simpler record systems in mainstream languages. If records are not as light-weight (syntactically as well as wrt run-time performance) as 'normal' Haskell data types, then people will be reluctant to use them, especially in library APIs. Finally, having to wait for highly experimental additional extensions to be available, tried, and tested, would only help to indefinitely post- pone the introduction of a usable record system.
I totally disagree. The great strength of Haskell is that, whenever important design decisions have been made, the primary consideration has not been practicality, but generality and mathematical foundation. When the Haskell committee first started work, many people said lazy evaluation was an academic curiosity: mathematically right, but far too inefficient for real programs. When Haskell adopted type classes, people said they were far too heavy a machinery to solve the relatively simple problems of equality, show and numbers. In each case the more general, abstract approach has shown enormous advantages in the long term. I'm sure the same will be true of associated types, which are a lot more complex than functional dependencies, but also more general, and more mathematical.
Just to make that point clear: I am not in any way opposed to associated types, I agree thet they are nicer than fundeps. And anyway experimental extensions are Haskell's bread and butter. I just had the impression that they are very new and that not all the issues surrounding them have been solved.
My criteria for choosing a record system would be: continue with the philosophy which has served Haskell so well up to now. In other words, choose the system which is most general and most mathematically sound; get some kind of implementation working so that we can get some experience with using it; then worry about efficiency later.
You are making a good point. Indeed, I feel like getting my own words thrown back to me, as I have often been arguing against 'practical' proposals with similar arguments. It reassures me a little bit that you are so convinced your library / associated types based proposal is actually better than the simple but admittedly more ad-hoc solution like SPJ's old proposal. I still fear that, better or not, appropriate support for your proposal will not be implemented in the forseeable future. So, maybe I just have to be more patient and wait yet another few years for Haskell records. Cheers Ben
Dear Fellows, It is obvious that Haskell takes its strength from well implemented abstract concepts that made monadic programming natural and threads cheap. However I believe that some top decisions have been made in the wrong order, which is extremely important now to be aware of when new feature requests gather speed from the new breed of Haskell users - the real programmers. Haskell has jumped into untested water of type classes and had to compromise module system, contrary to ocaml where functor was raised to primary unit of computation. Thus Haskell waved off warranty form mature category theory for the sake of experimenting with fledging type theory. This encouraged somewhat artistic approach in designing the whole syntax so that orthogonality is hard to find and as a result choosing a particular programming style is more cumbersome than type annotations. In my humble opinion the priorities now should be: - moving secondary features from compiler out to optional libraries - redesigning type classes so that they could evolve with minimal effect on core design - revisiting SPJ opinion that functors are an (unpractical and expensive) Ferrari. I would rather paraphrase Henri Ford's "every color for a car is good as long as it is black" and say that every functional language is good as long as it has ML-style module system. I expressed this opinion here a year ago but as a mere mathematician haven't felt moral right to expect much attention. Recently however may opinion gained a surprising backup form guru Okasaki http://okasaki.blogspot.com/2008/02/ten-years-of-purely-functional-data.html Regards, -Andrzej Jaworski
Barney Hilken wrote:
I totally disagree. The great strength of Haskell is that, whenever important design decisions have been made, the primary consideration has not been practicality, but generality and mathematical foundation. When the Haskell committee first started work, many people said lazy evaluation was an academic curiosity: mathematically right, but far too inefficient for real programs. When Haskell adopted type classes, people said they were far too heavy a machinery to solve the relatively simple problems of equality, show and numbers. In each case the more general, abstract approach has shown enormous advantages in the long term. I'm sure the same will be true of associated types, which are a lot more complex than functional dependencies, but also more general, and more mathematical.
While I agree with your general argument, I wonder if you realize that functional dependencies have a strong, general, and elegant mathematical foundation that long predates their use in Haskell? If you want even a brief glimpse, there's s short article at http://en.wikipedia.org/wiki/Functional_dependencies that might give you some ideas. The mathematics of functional dependencies plays an important role in the theory of relational databases. I don't know what you consider as the mathematical foundations for associated types, nor do I know why you consider that to be either more general or more "mathematical" (whatever that means) but I hope you'll enjoy the material on functional dependencies. All the best, Mark
While I agree with your general argument, I wonder if you realize that functional dependencies have a strong, general, and elegant mathematical foundation that long predates their use in Haskell? If you want even a brief glimpse, there's s short article at http://en.wikipedia.org/wiki/Functional_dependencies that might give you some ideas. The mathematics of functional dependencies plays an important role in the theory of relational databases.
I don't know what you consider as the mathematical foundations for associated types, nor do I know why you consider that to be either more general or more "mathematical" (whatever that means) but I hope you'll enjoy the material on functional dependencies.
I admit I was being unfair on fundeps in calling them less mathematical. Nonetheless, something about their addition to Haskell grates on my mathematical sensitivities. They feel "bolted on" in a way that associated types don't. Probably this is because of my own bias which leads me to see Haskell as a subset of dependent type theory, and ATs as some kind of sigma type (though I've never tried to make this precise). Barney.
On Feb 5, 2008 2:28 AM, Bulat Ziganshin <bulat.ziganshin@gmail.com> wrote:
this principle allows to build programs in quick and easy way: we just add to objects implementations of all the methods required:
e = new Entry {label := "Hi", color := blue, getValue := getEntryValue, setValue := setEntryValue}
e.display -- uses color/label properties e.saveToFile -- uses getValue property
I'm new to all this -- I can't figure out why we want to put methods inside of records. Why don't we define a module instead? module EntryModule where data Entry = Entry String Color display (Entry s c) = do someIOMagic saveToFile (Entry s c) = do someOtherIOMagic If we want a more generic approach -- where a function excepts one of many kinds of data -- than is a type class not suitable? -- _jsn
participants (14)
-
Andrzej Jaworski -
Barney Hilken -
Ben Franksen -
Brandon S. Allbery KF8NH -
Bulat Ziganshin -
Cale Gibbard -
Dan Weston -
Jason Dusek -
Johannes Waldmann -
John Meacham -
Jonathan Cast -
Mark P Jones -
Sebastian Sylvan -
Simon Peyton-Jones