Search code examples
functional-programmingpurescript

Compiler can't infer type while using Newtype class Constraint purescript


I was trying to write a function that can get the data that a newtype wraps.

newtype Person = Person {name :: String, age :: Int}

getRecord :: forall r. Newtype Person r => Person -> r
getRecord (Person p) = p

This fails to compile and complains

Could not match type

    { age :: Int
    , name :: String
    }

  with type

    r0


while checking that type { age :: Int
                         , name :: String
                         }
  is at least as general as type r0
while checking that expression p
  has type r0
in value declaration getRecord

But when I do

getRecord :: forall r. Newtype Person r => Person -> r
getRecord  = unwrap 

It works fine !! My Question is why it can't infer the type the first time?

The newtype type class is defined like,

class (Coercible t a) <= Newtype t a | t -> a

which means it can infer the a when it knows what t is. Then why its not working the first time.


Solution

  • Constraint solving happens when calling the function, but not when defining one.

    At call site, the compiler will solve the Newtype Person r constraint and figure out that r ~ { age :: Int, name :: String }, so you can use its fields.

    But at definition site, the compiler doesn't do that. There, it's just some unknown type r. And since it's unknown, the compiler can't in good faith tell that r ~ { age :: Int, name :: String }, so there is a type mismatch.


    Using unwrap works, because there the type r doesn't need to be known. Whatever it is, it's just passed straight to unwrap, and your function doesn't need to know anything about it.


    Why does the compiler not do constraint solving at definition site? I mean it technically could, but that would be kinda useless anyway: after all, if you already know the type, just write it out:

    getRecord :: Person -> { age :: Int, name :: String }
    getRecord (Person p) = p