Calling a function `f` within another function `g` that takes a record which is the subset of the `f`'s parameter

I have this definitions:

type Row1 r = (a :: String, b :: Int | r)
type Row2 r = (c :: String, d :: Number | r)
type AllKeys = Row1 (Row2 ())

I want two functions g for Row1 and h for Row2 both call function f for AllKeys. So I write f as follows:

f
  :: forall input interm
   . Union input AllKeys interm
  => Nub interm AllKeys
  => Array { | input }
  -> Unit
f _ = unit

I can call f with any combination of keys.

Then I write g (respectively h) as:

g
  :: forall input interm
   . Union input (Row1 ()) interm
  => Nub interm (Row1 ())
  => Array { | input }
  -> Unit
g _ = f -- <=== error

I get this error

Could not match type
    Array (Record t0) -> Unit
  with type
    Unit
while checking that type forall (input :: Row Type) (interm :: Row Type).
    Union @Type input
        ( a :: String
        , b :: Int
        , c :: String
        , d :: Number
        )
        interm
    => Nub @Type interm
            ( a :: String
            , b :: Int
            , c :: String
            , d :: Number
            )
        => Array (Record input) -> Unit
  is at least as general as type Unit
while checking that expression f
  has type Unit
in value declaration h
where t0 is an unknown type

I thought that I need to prove that input is also a subset of AllKeys so I changed the signature to

g
  :: forall input interm interm2
   . Union input (Row1 ()) interm
  => Nub interm (Row1 ())
  => Union input AllKeys interm2
  => Nub interm2 AllKeys
  => Array { | input }
  -> Unit

But this didn’t change the error either. What can I do to call two functions with parameters that are subsets of AllKeys and functions themselves call f?

Do you have a specific use case you are working with? From your description, it’s not obvious why you are involving these constraints. If you have a function f :: { | AllKeys } -> Foo, and both g and f need to call f, then they either need to also take { | AllKeys } or they need to be able to construct an { | AllKeys } by providing values for the extra fields. You can’t call f with a subset of fields, because that isn’t type correct since the fields don’t exist but the type says they must.

Sorry for insufficient explanation, not a native English speaker here :frowning:

AllKeys contains keys from both Row1 and Row2. I have two functions say g and h which accepts record containing subset of keys of Row1 and Row2 respectively. Both functions call a function f which takes a record with a subset of keys of AllKeys.

If I define f as in my previous message. I can call f without problem with any subset of the keys in AllKeys.

But if I call f from g or h error raise. So how can I call a function f that has a record parameter which has a subset of keys of AllKeys, from within a function that takes a subset of keys of Row1 or Row2?

I hope I can explain it properly.

What I trying to do is that I want to do some computation related for say Row1 (say provides some defaults for missing keys) and send the record to a function that completes the computation (say provides some further defaults). This function can be called from another function that does the same thing for Row2.

In general, my suggestion is “don’t do that”, because you are trying to model sub-typing relationships, which the compiler does not understand, and Union is not enough to express that conveniently in the general case. There is no way for the compiler to accept this type without unsafeCoerce because Union only computes types, it does not provide evidence of a structural coercion. If your f in this case is some FFI function, where it is using the “Union” hack to represent optional fields, then you should treat this pattern as first-order. It only really works when you are calling the function with a known, concrete type.

If you’d like an example of these kinds of APIs in PureScript semantics, maybe try something like GitHub - natefaubion/purescript-convertable-options: Highly-overloaded APIs for PureScript. It’s not particularly convenient to define, but it does not use sub-typing relationships.

Thank you very much for your pointer.

I’ll look at that package for inspiration. But sometimes it is hard for me to understand some type tango. I ask for answers if that will be the case.

Thanks again.