Overloaded record fields

AntC anthony_clayden at clear.net.nz
Fri Jun 28 13:16:48 CEST 2013


> Simon Peyton-Jones <simonpj <at> microsoft.com> writes:
> 
> I have, however, realised why I liked the dot idea.  Consider
> 
> 	f r b = r.foo && b
> 

Thanks Simon, I'm a little puzzled what your worry is.

> With dot-notation baked in (non-orthogonally), f would get the type
>
>	f :: (r { foo::Bool }) => r -> Bool -> Bool
> 
> With the orthogonal proposal, f is equivalent to
> 	f r b = foo r && b
> 
> Now it depends. 
> 
> * If there is at least one record in scope with a field "foo" 
>   and no other foo's, then you get the above type
> 

I don't think the compiler has to go hunting for 'records in scope'.
There is one of two situations in force:

Step 6. -XNoMonoRecordFields  
        Then function foo is not defined.
        (Or at least not by the record fields mechanism.)
        This is exactly so that the program can define
        its own access method (perhaps lenses,
         perhaps a function foo with a different type,
         the namespace is free for experiments).

Step 7. -XPolyRecordFields
        Then function foo is defined with the same type
        as would be for (.foo) in the baked-in approach. IOW

 	f r b = (.foo) r && b     -- baked-in
        f r b = foo r && b        -- non-baked-in, as you put

        foo = getFld :: (r { foo :: Bool } ) => r -> Bool

        So the type you give would be inferred for function f.

        At the use site for f (say applied to record type Bar).
        We need:

            instance (t ~ Bool) => Has Bar "foo" t where ...

        So generate that on-the-fly.


If the program declares a separate function foo,
then we have 'vanilla' name clash, just like double-declaring any name.
(Just like a H98 record with field foo, then declaring a function foo.)


Or is the potential difficulty something like this:

+ function f is defined as above in a module with -XPolyRecordFields.
+ function f is exported/imported.
+ the importing module also uses -XPolyRecordFields.
+ now in the importing module we try to apply f to a record.
  (say type Baz, not having field foo)
+ the compiler sees the (r { foo :: Bool }) constraint from f.

The compiler tries to generate on-the-fly:

    instance (t ~ Bool) => Has Baz "foo" t where
        getFld (MkBaz { foo = foo }) = foo  -- no such field

    But this could happen within a single module.
    At this point, we need Adam to issue a really clear error message.


Or perhaps the importing module uses H98 records.
And it applies f to a record type Baz.
And there is a field foo type Bool in data type Baz.
Then there's a function:

    foo :: Baz -> Bool       -- H98 field selector

Now we _could_ generate an instance `Has Baz "foo" t`.
And it wouldn't clash with Mono field selector foo.

But the extension is switched off. So we'll get:

    No instance `Has Baz "foo" t` arising from the use of `f` ...



(It's this scenario that led me to suggest in step 7
that when exporting field foo,
_don't_ export field selector function foo.)


> 
> This raises the funny possibility that you might have to define a local 
type
> 	data Unused = U { foo :: Int }
> simply so that there *is* at least on "foo" field in scope.
> 

No, I don't see that funny decls are needed.


AntC

> 
> | -----Original Message-----
> | From: glasgow-haskell-users On Behalf Of AntC
> | Sent: 27 June 2013 13:37
> | 
> | 7. Implement -XPolyRecordFields, not quite per Plan.
> |    This generates a poly-record field selector function:
> | 
> |        x :: r {x :: t} => r -> t    -- Has r "x" t => ...
> |        x = getFld
> | 
> |     And means that H98 syntax still works:
> | 
> |        x e     -- we must know e's type to pick which instance
> | 
> |     But note that it must generate only one definition
> |     for the whole module, even if x is declared in multiple data types.
> |     (Or in both a declared and an imported.)
> | 
> |     But not per the Plan:
> |     Do _not_ export the generated field selector functions.
> |     (If an importing module wants field selectors,
> |      it must set the extension, and generate them for imported data
> | types.
> |      Otherwise we risk name clash on the import.
> |      This effectively blocks H98-style modules
> |      from using the 'new' record selectors, I fear.)
> |     Or perhaps I mean that the importing module could choose
> |     whether to bring in the field selector function??
> |     Or perhaps we export/import-control the selector function
> |     separately to the record and field name???
> | 





More information about the Glasgow-haskell-users mailing list