purescript / purescript-typelevel-prelude

Types and kinds for basic type-level programming
BSD 3-Clause "New" or "Revised" License
63 stars 21 forks source link

RowSingleton class #16

Closed MonoidMusician closed 3 years ago

MonoidMusician commented 7 years ago

I think this is a general-purpose operation that can go in with the row operations. Thoughts?

My use case is extracting a named value in tests: {acopy} `asserteq` 1 can display a nice error message that is guaranteed to match the variable name. There's probably other uses for it, but I think it is nice just because it fills in the semantic gap/limitation of RowCons.

C.f. https://github.com/purescript/purescript/issues/2809

paf31 commented 7 years ago

Looks good, but do we need the RowCons superclass?

MonoidMusician commented 7 years ago

It's not strictly necessary, but I think it makes sense as a nice abstraction that saves writing the implied RowCons, especially for the common case of using Data.Record.get. Up to you, though! (Data.Record could always have a separate function for this case if we want to avoid the superclass....)

MonoidMusician commented 7 years ago

Wait, the fundeps should probably be -> label typ row, since there is only one instance ... still learning the nuances here!

paf31 commented 7 years ago

No, the fundeps should be row -> label typ and label typ -> row. That is, there is a correspondence between singleton rows and pairs of a label and a type.

What you have now says I can guess all three given no information.

LiamGoodacre commented 7 years ago

I think the difference between the fundeps would be which type classes are mentioned in the error? For row -> label typ, label typ -> row you'd get no instance found for RowSingleton, but with -> row label typ the user would get no instance for either RowToList or RowCons - because there's not enough information to satisfy the instance constraints, but this instance is always selected.

MonoidMusician commented 7 years ago

Hm.... testing with this function, I see no differences in the type errors with either of the fundeps:

getlabeltype :: forall label typ row. RowSingleton label typ row => RProxy row -> Tuple (SProxy label) (TProxy typ)
getlabeltype _ = Tuple SProxy TProxy

getlabeltype (RProxy :: RProxy ())

  Could not match type     
    Nil     
  with type
    Cons t0 t1 Nil 
while solving type class constraint           
  Type.Row.RowToList ()              
                     (Cons t0 t1 Nil)
while applying a function getlabeltype
  of type RowSingleton t0 t1 t2 => RProxy t2 -> Tuple (SProxy t0) (TProxy t1)
  to argument RProxy
while inferring the type of getlabeltype RProxy

But I did notice that I needed to export it!

MonoidMusician commented 7 years ago

FWIW I've been using a slightly modified RowSingleton with | -> label typ row with no problems in some silly lens code (converting between singleton records and variants).

I believe that @LiamGoodacre has said that fundeps in PS are different from Haskell and relate more to a covering set needed for selecting the instance, versus the notion that one variable directly determines the other. Thus I would say that no information is needed to determine the relationship of the three variables, which is given primarily by RowToList, and confirmed by RowCons.

paf31 commented 7 years ago

@MonoidMusician That example wouldn't show the issue @LiamGoodacre is talking about, but he's right, the errors will be different in some cases.