Search code examples
functional-programmingpurescript

Why purescript can infer the Row.Cons contraint?


I have a newtype which takes a parameterized type,

newtype MockState m = MockState { apiState :: {api1 :: m Int, api2:: m String} }
derive instance newtypeMockState :: Newtype (MockState m) _

I have a function that creates a ref out of some field,

createRef :: forall s a b r r' rem 
        .  IsSymbol s
        => Newtype (MockState Identity) {apiState :: Record r}
        => Row.Cons s (Identity a) r' r 
        => Proxy s 
        -> MockState Identity
        -> Effect (Ref.Ref a)
createRef pxy (MockState mockState)= do 
      let (Identity val) = Record.get pxy mockState.apiState
      Ref.new val

Why the compiler can't resolve the constraint?

  No type class instance was found for

    Prim.Row.Cons s4
                  (Identity a5)
                  t2
                  ( cla :: Identity String
                  , login :: Identity Int
                  )

When I pass Record r directly insted of my newtype, it compiles without any issue... and the below also works..

createRef :: forall s a b mock r' r rem 
        .  IsSymbol s
        => Newtype mock {apiState :: Record r}
        => Row.Cons s (Identity a) r' r 
        => Proxy s 
        -> mock
        -> Effect (Ref.Ref a)
createRef pxy r = do 
      let (Identity val) = Record.get pxy (unwrap r).apiState
      Ref.new val

but how? Literally the first one and last are same right? Why the compiler can't infer r?


Solution

  • This is because the compiler can't see a connection between your Newtype constraint and the pattern (MockState mockState). These are technically unrelated. The Newtype class is not magic enough for this.

    So mockState is inferred to be of type { apiState :: Record r1 }, where r1 is distinct from r, and therefore constraint Cons s (Identity a) r' r does not apply to r1.

    In order to get the right type inference, you have to unwrap your newtype by utilizing the Newtype constraint itself. Then the unwrapped value will have type { apiState :: Record r }, and the Cons constraint will apply.

    You can do this by using the unwrap function:

    createRef pxy mockState= do
          let (Identity val) = Record.get pxy (unwrap mockState).apiState
          Ref.new val