Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purescript Union of Rows

I've been trying to develop a component system in Purescript, using a Component typeclass which specifies an eval function. The eval function for can be recursively called by a component for each sub-component of the component, in essence fetching the input's values.

As components may wish to use run-time values, a record is also passed into eval. My goal is for the rows in the Record argument of the top-level eval to be required to include all the rows of every sub-component. This is not too difficult for components which do not use any rows themselves, but their single sub-component does, as we can simply pass along the sub-components rows to the component's. This is shown in evalIncrement.

import Prelude ((+), one)
import Data.Symbol (class IsSymbol, SProxy(..))
import Record (get)
import Prim.Row (class Cons, class Union)

class Component a b c | a -> b where
  eval :: a -> Record c -> b

data Const a = Const a

instance evalConst :: Component (Const a) a r where
  eval (Const v) r = v

data Var (a::Symbol) (b::Type) = Var

instance evalVar :: 
  ( IsSymbol a
  , Cons a b r' r) => Component (Var a b) b r  where
  eval _ r = get (SProxy :: SProxy a) r

data Inc a = Inc a

instance evalInc :: 
  ( Component a Int r
  ) => Component (Inc a) Int r where
  eval (Inc a) r = (eval a r) + one

All of the above code works correctly. However, once I try to introduce a component which takes multiple input components and merges their rows, I cannot seem to get it to work. For example, when trying to use the class Union from Prim.Row:

data Add a b = Add a b

instance evalAdd :: 
  ( Component a Int r1
  , Component b Int r2
  , Union r1 r2 r3
  ) => Component (Add a b) Int r3 where
  eval (Add a b) r = (eval a r) + (eval b r)

The following error is produced:

  No type class instance was found for

    Processor.Component a3 
                        Int 
                        r35


while applying a function eval
  of type Component t0 t1 t2 => t0 -> { | t2 } -> t1
  to argument a
while inferring the type of eval a
in value declaration evalAdd

where a3 is a rigid type variable
      r35 is a rigid type variable
      t0 is an unknown type
      t1 is an unknown type
      t2 is an unknown type

In fact, even modifying the evalInc instance to use a dummy Union with an empty row produces a similar error, like so:

instance evalInc :: (Component a Int r, Union r () r1) 
                       => Component (Increment a) Int r1 where

Am I using Union incorrectly? Or do I need further functional dependencies for my class - I do not understand them very well.

I am using purs version 0.12.0

like image 236
Joseph Young Avatar asked Jul 19 '18 12:07

Joseph Young


Video Answer


1 Answers

r ∷ r3 but it is being used where an r1 and r2 are required, so there is a type mismatch. A record {a ∷ A, b ∷ B} cannot be given where {a ∷ A} or {b ∷ B} or {} is expected. However, one can say this:

f ∷ ∀ s r. Row.Cons "a" A s r ⇒ Record r → A
f {a} = a

In words, f is a function polymorphic on any record containing a label "a" with type A. Similarly, you could change eval to:

eval ∷ ∀ s r. Row.Union c s r ⇒ a → Record r → b

In words, eval is polymorphic on any record which contains at least the fields of c. This introduces a type ambiguity which you will have to resolve with a proxy.

eval ∷ ∀ proxy s r. Row.Union c s r ⇒ proxy c → a → Record r → b

The eval instance of Add becomes:

instance evalAdd ∷
  ( Component a Int r1
  , Component b Int r2
  , Union r1 s1 r3
  , Union r2 s2 r3
  ) => Component (Add a b) Int r3 where
  eval _ (Add a b) r = eval (RProxy ∷ RProxy r1) a r + eval (RProxy ∷ RProxy r2) b r

From here, r1 and r2 become ambiguous because they're not determined from r3 alone. With the given constraints, s1 and s2 would also have to be known. Possibly there is a functional dependency you could add. I am not sure what is appropriate because I am not sure what the objectives are of the program you are designing.

like image 99
erisco Avatar answered Sep 19 '22 17:09

erisco