Records in Haskell
Greg Weber
greg at gregweber.info
Thu Jan 26 16:19:56 CET 2012
The starting point a new records implementation was to be pragmatic
and get something done. Simon has identified that Has constraints are
required to implement records.
However, in general, exposing an internal implementation to a user is
an idea that should give great pause. It makes it difficult to switch
implementations in the future, and there is likely a more ideal
interface for the end user - I believe that is the case for Has
constraints.
So I propose we move forward with implementing Has constraints, but do
not expose it to the user (similar to TDNR). We don't add abstraction
over fields or virtual fields or any special capabilities that would
expose the implementation. After we have a working records
implementation that meets our goal of solving name-spacing, we can
re-visit what the best interface is to present to the user for more
advanced records capabilities.
There were complaints about TDNR previously. I think we can address
them by noting that we are limiting the scope to just records and that
it is just a beginning that will not limit us.
With this in mind, what limitations are left? are updates still a
problem, and can we solve it (for now at least) by being more
demanding of type annotations?
Greg Weber
On Fri, Jan 20, 2012 at 3:22 PM, Greg Weber <greg at gregweber.info> wrote:
> 2012/1/18 Simon Peyton-Jones <simonpj at microsoft.com>:
>> | > Has *is* a type class. It can be used and abused like any other.
>> | > Record members with the same key ought to have the same semantics; the
>> | > programmer must ensure this, not just call them all "x" or the like.
>> | >
>> | > Weak types these are not. The selector type is well-defined. The value
>> | > type is well-defined. The record type is well-defined, but of course
>> | > we define a type-class to let it be polymorphic.
>>
>> I want to mention that the issue Greg raises here is tackled under "Representation hiding".
>>
>> The way we currently prevent random clients of a data type from selecting its "foo" field is by hiding the record selector "foo". Similarly for its data constructors. This is Haskell's way of doing data abstraction; it may not be the best way, but it's Haskell's way.
>>
>> The trouble with instance declarations is that they are *always* exported. No hiding.
>>
>> Under "Representation hiding" I suggest that
>>
>> * If the record selector "foo" is in scope (by any name),
>> then the corresponding Has instance is in scope too
>> and vice versa.
>>
>> That would match up with Haskell's hiding mechanisms precisely, albeit at the cost of having an ad-hoc rule for "Has" instances.
>>
>> Simon
>>
>
> I am not just concerned about Has instances from modules not under my control.
> I am concerned there is a new implicit rule that must be explained to
> me and that I have no control over: that every record field in the
> same module with a given label must have the exact same semantics as
> all others with the same label.
>
> Currently we don't have this rule. Currently we have records with the
> essentialy the same field labels, but with prefixes so they can
> coexist, and they need not have the same semantics.
>
> There is an extra problem with adding this rule: it is common practice
> to put types that arguably should be spread across multiple modules
> into one (module often named Types). Some form of this must be done
> out of necessity in the case of circular references between types that
> will otherwise not resolve.
>
> By default in Yesod, we do this out of necessity for records
> representing database tables. We call the module Models and users are
> free to write functions there. Now we have to explain to them that
> they should not.
>
> What use case for abstraction over record fields is too burdensome to
> use a type class for? Lacking such a compelling use case we are adding
> implicit complexity that can result in weak-typing for the unwary user
> with no compelling benefit.
>
> The wiki page for overloaded records suggests that users are expected
> to write code like this for virtual selectors:
>
> instance Has Shape "area" Float where
> get = area
>
> This is a leaky abstraction forcing me to quote function names at the
> type level. I would rather not be writing this code - explaining this
> code to someone means delving into implementation details that I don't
> understand.
>
> The overloaded records proposal is vying for "extensibility" by
> supporting abstraction over record fields with the same name. For this
> dubious use case that type classes already satisfies we lose
> user-friendly implementation of obviously useful functionality like
> virtual selectors, and also explainability.
>
> There is something to be said for the fact that I can quickly
> comprehend explanations of module-based record systems. I have read
> over the Overloaded Records proposal several times now and I have the
> gist of it, but I am still confused on details and need another couple
> reads to look at all the details instead of glossing over them.
>
> So far I have found some details on record implementations in four FP
> languages. Every single one implements a module-like namespace for
> records, one after abandoning the abstraction over fields approach.
> There are differing good approaches to convenient access - I think
> that is where it is appropriate for Haskell to attempt to take a
> different approach.
More information about the Glasgow-haskell-users
mailing list