Search code examples
purescriptpolymorphic-variants

Combining lists of variants of different types in purescript


I have been playing around with Purescript as a means of experimenting with programming in a Haskell-like language with row and column polymorphism.

In particular, using Purescript's variant package, I was trying to write an operation to combine two lists list of "notes" sequentially, where the two lists have possibly disjoint sets of variants. The idea here is to allow users to be able to combine scores declared on some sub-set of notes to be automatically combined with scores containing a super-set, without preforming any explicit conversions.

For example, given the following boilerplate to make working with the variants easier:

import Data.Variant
import Prim.Row

data A = A
data B = B
data C = C
data D = D
data E = E
data F = F
data G = G
data H = H

_A = SProxy :: SProxy "_A"
_B = SProxy :: SProxy "_B"
_C = SProxy :: SProxy "_C"
_D = SProxy :: SProxy "_D"
_E =  SProxy :: SProxy "_E"
_F =  SProxy :: SProxy "_F"
_G =  SProxy :: SProxy "_G"

I can define the following types:

type CNatural = Variant (
    _A :: A,  
    _B :: B,
    _C :: C,
    _D :: D,
    _E :: E,
    _F :: F,
    _G :: G)
  
type CPentatonic = Variant (
    _A :: A,  
    _C :: C,
    _D :: D,
    _E :: E,
    _G :: G)

And given those, I can define two scores:

score1 :: Array CPentatonic  
score1 = [inj _A A,inj _C C]

score2 :: Array CNatural
score2 = [inj _C C, inj _B B]

So, to combine them, I want a function of signature

combine :: forall v w u. Union w v u => 
     Array (Variant v)
  -> Array (Variant w)
  -> Array (Variant u)

However, my attempt at this:

combine x y = (map expand x) <> (map expand y)

yields

  No type class instance was found for

    Prim.Row.Union v2
                   t3
                   u4

  The instance head contains unknown type variables. Consider adding a type annotation.

If I try changing the Union v w u constraint to a Union w v u constraint, the error goes back and forth between the first map expand, and the second map expand, but nothing I do seems to resolve both constraints, even if I have both a Union v w u and a Union w v u constraint, which I thought would have been redundant.

Is there something I'm missing? Is it possible to do something like this in Purescript using the variant library?


Solution

  • If you want to produce Variant u by using expand you have to require that both v and w are subrows of u:

    module Main where
    
    import Prelude
    
    import Data.Variant (expand, Variant)
    import Prim.Row (class Union)
    
    combine :: forall v v_ w w_ u. Union w w_ u => Union v v_ u =>
         Array (Variant v)
      -> Array (Variant w)
      -> Array (Variant u)
    combine arr1 arr2 = map expand arr1 <> map expand arr2
    

    Here is a working gist loaded into try.purescript.