... | @@ -13,7 +13,7 @@ The purpose of this page is to collect and discuss proposals for adding extensib |
... | @@ -13,7 +13,7 @@ The purpose of this page is to collect and discuss proposals for adding extensib |
|
- [ Scoped Labels](http://www.cs.uu.nl/~daan/download/papers/scopedlabels.pdf)
|
|
- [ Scoped Labels](http://www.cs.uu.nl/~daan/download/papers/scopedlabels.pdf)
|
|
- [ Type Families](http://homepage.ntlworld.com/b.hilken/files/Records.hs)
|
|
- [ Type Families](http://homepage.ntlworld.com/b.hilken/files/Records.hs)
|
|
- [ Heterogeneous Collections](http://homepages.cwi.nl/~ralf/HList/), see also [ Keyword Arguments](http://okmij.org/ftp/Haskell/keyword-arguments.lhs)
|
|
- [ Heterogeneous Collections](http://homepages.cwi.nl/~ralf/HList/), see also [ Keyword Arguments](http://okmij.org/ftp/Haskell/keyword-arguments.lhs)
|
|
- [ Data.Record.hs](http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/Data.Record.hs), expanded and documented version of the old Haskell prime ticket 92 attachment [ Poor Man's Records](http://hackage.haskell.org/trac/haskell-prime/attachment/ticket/92/Data.Record.hs)
|
|
- [ Data.Record.hs](http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/Data.Record.hs), expanded and documented version of the old Haskell prime ticket 92 attachment [ Data.Record.hs](http://hackage.haskell.org/trac/haskell-prime/attachment/ticket/92/Data.Record.hs). (comment: my preferences would be (1) we should try to implement as many useful record operations, predicates, and invariants as we can, (2) we should try to unify the sets of operations into a coherent whole, (3) we should identify to what extent and in what form we need to have language and implementation support, and (4) users, not library providers, will decide which subsets of operations they use most; a library providing for as many common usage patterns as possible might have a chance of breaking the deadlock, and laying the groundwork for a future design that might actually have some users and experience behind it; these preferences appear to conflict with the intentions of the creator of this page)
|
|
|
|
|
|
# Syntax
|
|
# Syntax
|
|
|
|
|
... | @@ -38,15 +38,20 @@ By **constant shape** we mean that the field names of a record are given literal |
... | @@ -38,15 +38,20 @@ By **constant shape** we mean that the field names of a record are given literal |
|
|
|
|
|
An important difference between the various proposals is what constitutes a valid record, and similarly a valid record type. The key points are:
|
|
An important difference between the various proposals is what constitutes a valid record, and similarly a valid record type. The key points are:
|
|
|
|
|
|
- Permutativity:: Are `{X :: Int, Y :: Int}` and `{Y :: Int, X :: Int}` the same type? The **Poor Man's Records** system distinguishes these two, which makes implementation much simpler, but means that any function which accepts permuted records must be polymorphic. *(i don't see why this is an issue? you can either list the fields you want as separate constraints, or you can have a single constraint requiring your record's type to be a permutation of the interface you want, or you can give a specific interface type and permute any record to that interface's field ordering, without ever requiring a global ordering on field types, as most other proposals do; i've added some `Data.Record` versions of your example, to demonstrate)*
|
|
- Permutativity:: Are `{X :: Int, Y :: Int}` and `{Y :: Int, X :: Int}` the same type? The **Poor Man's Records** system distinguishes these two, which makes implementation much simpler, but means that any function which accepts permuted records must be polymorphic.
|
|
- Repeated Fields:: Is `{X :: Int, X :: Int}` a valid record type? Both **Poor Man's Records** and **Scoped Labels** allow this type, but other systems consider this an error. *(note that **Poor Man's Records** can handle both scoped and unscoped records: if you start from non-scoped records and only apply non-scoped record operations, your records will remain unscoped. you can also easily define a type predicate that guarantees your record to be unscoped. i accept, however, that a non-scoped-only style should be better supported, so i've added such a predicate for the next version of the library, and i'd like to add a type tag that turns this predicate into an invariant.)*
|
|
- Repeated Fields:: Is `{X :: Int, X :: Int}` a valid record type? Both **Poor Man's Records** and **Scoped Labels** allow this type, but other systems consider this an error.
|
|
|
|
|
|
<table><tr><th>Ok, opinion (since you asked for it!)</th>
|
|
<table><tr><th>Ok, opinion (since you asked for it!)</th>
|
|
<td>The problem with scoping is that, in most cases, repeated fields are a programmer error, and the point of types is to catch such errors at compile time. At first sight, the ability to scope fields in this way looks like extra power to the programmer, but is this actually useful? I've seen no convincing examples where scoping allows a more clearly structured program, whereas there are plenty of cases where it will mean an error goes uncaught. If you have a good example of scoping, please add it to the examples section on this page. *(the way to think about scoped records is not as "repeated fields are required", but as "if repeated fields cause no errors, they are not ruled out"; an example would be `PATH` settings: you can make sure that you have only one version of each tool in `PATH`, but most of the time you just move the version you want right now to the front)*</td></tr>
|
|
<td>The problem with scoping is that, in most cases, repeated fields are a programmer error, and the point of types is to catch such errors at compile time. At first sight, the ability to scope fields in this way looks like extra power to the programmer, but is this actually useful? I've seen no convincing examples where scoping allows a more clearly structured program, whereas there are plenty of cases where it will mean an error goes uncaught. If you have a good example of scoping, please add it to the examples section on this page.
|
|
<tr><th>.</th>
|
|
</td></tr></table>
|
|
<td>The problem with unpermuted records is illustrated in the example at the bottom of this page: it forces you to make functions polymorphic when they "ought" to be monomorphic. This means that no-one can use records in a straightforward way without understanding the details of all the predicates. *(predicates are parts of types, in particular, they restrict polymorphism. as the example shows, you can still be monomorphic if you absolutely want to, but understanding types, including predicates, is essential for understanding how to use haskell operations)*</td></tr>
|
|
|
|
<tr><th>.</th>
|
|
<table><tr><th>.</th>
|
|
<td>In both cases, the usual approach (permutation, no repeats) is what most programmers expect. We have to remember that this proposal is supposed to be *the* records system for Haskell. It must be the right system for simple problems as well as complex ones. *(if you are really still stuck at the "one system fits everyone" stage, i would find it difficult to continue any discussion; my premises in this round have been that (1) we should try to implement as many useful record operations, predicates, and invariants as we can, (2) we should try to unify the sets of operations into a coherent whole, (3) we should identify to what extent and in what form we need to have language and implementation support, and (4) users, not library providers, will decide which subsets of operations they use most; assuming that one particular view of records is "the usual one", or "what most programmers expect" has not helped in the past, and will not help now; just making yet-another-record-proposal is no more likely to succeed than any of the previous attempts, whereas a library providing for as many common usage patterns as possible might have a chance of breaking the deadlock, and laying the groundwork for a future design that might actually have some users and experience behind it; to be successful, such a design would evolve in use, rather than be proclaimed and ignored)*</td></tr></table>
|
|
<td>The problem with unpermuted records is illustrated in the example at the bottom of this page: it forces you to make functions polymorphic when they "ought" to be monomorphic. This means that no-one can use records in a straightforward way without understanding the details of all the predicates.
|
|
|
|
</td></tr></table>
|
|
|
|
|
|
|
|
<table><tr><th>.</th>
|
|
|
|
<td>In both cases, the usual approach (permutation, no repeats) is what most programmers expect. We have to remember that this proposal is supposed to be *the* records system for Haskell. It must be the right system for simple problems as well as complex ones.
|
|
|
|
</td></tr></table>
|
|
|
|
|
|
<table><tr><th>.</th>
|
|
<table><tr><th>.</th>
|
|
<td>I totally disagree. More operations aren't always better! What the language needs is a records system which allows and encourages you to structure your code in the clearest possible way. In my opinion, choosing a type system which behaves as expected is an important part of that, but I would quickly be convinced otherwise by some examples which show that these extra features help to write better code. Personal comments, on the other hand, don't help.
|
|
<td>I totally disagree. More operations aren't always better! What the language needs is a records system which allows and encourages you to structure your code in the clearest possible way. In my opinion, choosing a type system which behaves as expected is an important part of that, but I would quickly be convinced otherwise by some examples which show that these extra features help to write better code. Personal comments, on the other hand, don't help.
|
... | @@ -131,9 +136,12 @@ The `Subrecord` predicate and `<-` operator could easily be added. The differenc |
... | @@ -131,9 +136,12 @@ The `Subrecord` predicate and `<-` operator could easily be added. The differenc |
|
As it seems possible to implement most of the functionality in a library, there might be no need for a complex **extensible records** feature. Nevertheless, there are issues which are common to most proposals and which would best be addressed at the language and implementation level:
|
|
As it seems possible to implement most of the functionality in a library, there might be no need for a complex **extensible records** feature. Nevertheless, there are issues which are common to most proposals and which would best be addressed at the language and implementation level:
|
|
|
|
|
|
- type sharing: not specific to records, but crucial for record programming practice. If multiple modules introduce the "same" labels, means are needed to specify the equivalence of these types (cf [ Haskell prime ticket 92](http://hackage.haskell.org/trac/haskell-prime/ticket/92)).
|
|
- type sharing: not specific to records, but crucial for record programming practice. If multiple modules introduce the "same" labels, means are needed to specify the equivalence of these types (cf [ Haskell prime ticket 92](http://hackage.haskell.org/trac/haskell-prime/ticket/92)).
|
|
|
|
|
|
- partial evaluation of type class programs: to achieve constant time record field access. Again, this feature is not specific to records, but crucial for record programming practice.
|
|
- partial evaluation of type class programs: to achieve constant time record field access. Again, this feature is not specific to records, but crucial for record programming practice.
|
|
|
|
|
|
- portability: it would be nice if extensible records libraries were portable over multiple Haskell implementations. That not only means that these implementations need to support the same features, but that they need to interpret these features in the same way (this is currently not the case for the interaction of functional dependencies and type class overlap resolution in GHC and Hugs).
|
|
- portability: it would be nice if extensible records libraries were portable over multiple Haskell implementations. That not only means that these implementations need to support the same features, but that they need to interpret these features in the same way (this is currently not the case for the interaction of functional dependencies and type class overlap resolution in GHC and Hugs).
|
|
- permutativity: The easiest way to implement permutativity of field labels is to sort them by some total ordering. Although this can be implemented using functional dependencies, it's complex and inefficient. Compiler support for a global order on tycons (based on fully qualified name, perhaps) would be very helpful. I have submitted a feature request [\#1894](https://gitlab.haskell.org//ghc/ghc/issues/1894)*(could you please expand on why you consider sorting necessary? the proposal in [\#1894](https://gitlab.haskell.org//ghc/ghc/issues/1894) would have some rather unfortunate consequences, and it does seem that we can make do without implicit sorting)*. Does this conflict with type sharing?
|
|
|
|
|
|
- permutativity: The easiest way to implement permutativity of field labels is to sort them by some total ordering. Although this can be implemented using functional dependencies, it's complex and inefficient. Compiler support for a global order on tycons (based on fully qualified name, perhaps) would be very helpful. I have submitted a feature request [\#1894](https://gitlab.haskell.org//ghc/ghc/issues/1894). Does this conflict with type sharing?
|
|
|
|
|
|
# Examples
|
|
# Examples
|
|
|
|
|
... | | ... | |