Help with extensible records


#1

I’m wanting to pass bigger records in to functions expecting smaller ones. My real world context is that I want to pass in API request functions to React.Basic components via props, with each component declaring just which API calls it will make, and the top-level application passing in all the API calls in a record. But I’ve stripped it down to this, and I’m failing to understand the problem, evidently:

module Data.Api (Small, Big, consumeSmall, consumeBig) where

import Prelude

type Small = forall r.
  { a :: Int
  , b :: Int
  | r
  }

type Big =
  { a :: Int
  , b :: Int
  , c :: Int
  }

consumeSmall :: Small -> Int
consumeSmall s = s.a + s.b

consumeBig :: Big -> Int
consumeBig b = consumeSmall b

The compilation error (with Purescript 0.12.2) is:

kiai$ pulp build
* Building project in /home/sjg/share/playpen/purescript/hello-world
Compiling Data.Api
Error found:
in module Data.Api
at src/Data/Api.purs:21:29 - 21:29 (line 21, column 29 - line 21, column 29)

  Could not match type
              
    ( c :: Int
    )         
              
  with type
      
    r0
      

while checking that type { a :: Int
                         , b :: Int
                         , c :: Int
                         }         
  is at least as general as type { a :: Int
                                 , b :: Int
                                 | r0      
                                 }         
while checking that expression b
  has type { a :: Int
           , b :: Int
           | r0      
           }         
in value declaration consumeBig

where r0 is a rigid type variable
        bound at (line 21, column 29 - line 21, column 29)

See https://github.com/purescript/documentation/blob/master/errors/TypesDoNotUnify.md for more information,
or to contribute content related to this error.


* ERROR: Subcommand terminated with exit code 1

I’m using https://leanpub.com/purescript/read#leanpub-auto-record-patterns-and-row-polymorphism to guide me.

Any pointers, please?

cheers,
Simon


#2

Should be

type Small r =
  { a :: Int
  , b :: Int
  | r
  }

And your type signature should be:

consumeSmall :: forall r. Small r -> Int

Putting the forall inside the type synonym is the equivalent of:

consumeSmall :: (forall r. { ... | r}) -> Int

Which isn’t the same thing (it’s like it’s nested in parens). This is a very common way to stub your toe as a beginner.


#3

Thanks! It’s all working nicely now, including my flexible approach to props.