0

In a nutshell

Given this function:

> :i set
set ::
  ( VividAction m                    -- This isn't important.
  , Subset (InnerVars params) sdArgs -- This says `params` is a subset of
                                     -- `sdArgs`, perhaps in a 
                                     -- different order.
  , VarList params) =>               -- A `VarList` is just a tuple.
  Synth sdArgs -> params -> m ()

and two values params and params' that satisfy the second constraint, is it possible to create a third value params'' that combines the information from params and params'?

In detail

I use the Vivid library to control SuperCollider from Haskell. Vivid lets you create a "synth", something that makes sound and accepts a number of parameters, and then send messages to it.

Those messages are typed. Sending messages to a synth looks like this:

> :set -XDataKinds
> set mySynth (120 :: I "frequency", 0.1 :: I "amplitude")
> set mySynth () -- Also valid. Messages can have any number of terms.

where "frequency" and "amplitude" should be two of the parameters that mySynth accepts. If you ask to manipulate a parameter that is not part of the definition of mySynth, Vivid will complain at compile time. This is reflected in the type of the function set:

> :i set
set ::
  ( VividAction m                    -- This isn't important.
  , Subset (InnerVars params) sdArgs -- This says `params` is a subset of
                                     -- `sdArgs`, perhaps in a 
                                     -- different order.
  , VarList params) =>               -- A `VarList` is just a tuple.
  Synth sdArgs -> params -> m ()
        -- Defined in ‘Vivid.Actions’

sdArgs represents the parameters that the synth accepts, and Subset (InnerVars params) sdArgs says that params must be a subset of those. (I believe most, maybe all, of Vivid's fancy type-level code is in the Vivid.SynthDef.TypesafeArgs module.)

My problem is that I'm generating (from a controller called a monome grid, using my library Montevideo) a lot of simultaneous singleton messages like (120 :: I "frequency"). The number of messages seems like it might be overwhelming SuperCollider -- I'm getting dropped notes and hung notes, which to me suggests dropped messages. I'd like to reduce the program's bandwidth by sending those simultaneous messages as one big tuple, rather than sending each one separately. But I don't know what type the combined tuple would be, so I can't create it!

I'd like to write a function like this:

concatTuples ::
  ( Subset (InnerVars params) sdArgs
  , VarList params) =>
  [params] -> params

That way, if my synth accepts "freq" and "amp" and "wobble", I can call concatTuples [(1 :: I "wobble"), (440 :: I "freq")] and expect to get either (1 :: I "wobble", 440 :: I "freq) or the swap of that.

Can concatTuples be written?

(EDIT: Joachim Breitner explains that that type signature I've suggested for concatTuples doesn't make sense. But maybe another one does?)

Something unimportant

An implementation question that arises is how to combine two tuples if they contain elements of the same type -- e.g. both have a frequency. I don't care how that's solved -- use the first, use the second, die -- because it's not a case I'll run into.

Jeffrey Benjamin Brown
  • 3,427
  • 2
  • 28
  • 40
  • NB: An answer to this question is necessary specific to vivid or supercollider. – Joachim Breitner Sep 02 '20 at 10:07
  • The question is motivated by SuperCollider and Vivid, but the answer has nothing to do with either of them. I'm only asking how to combine tuples. The `I` type carries no information except its phantom `String` parameter. Elements of type `I :: "frequency"` or `I :: "gonnorhea"` are just `Float`s with labels attached. – Jeffrey Benjamin Brown Sep 03 '20 at 01:20
  • Ah, but your `concatTuples` doesn't take tuples, it takes arbitrary types constraints by `VarList`. So unless `VarList` gives you such an ability somehow, you can’t. Which library does `VarList` come from? Guess it’s here: https://hackage.haskell.org/package/vivid-0.4.2.3/docs/Vivid-SynthDef-TypesafeArgs.html#t:VarList – Joachim Breitner Sep 03 '20 at 07:04

1 Answers1

1

Focusing just on

I'd like to write a function like this:

concatTuples ::
 ( Subset (InnerVars params) sdArgs
 , VarList params) =>
     [params] -> params

That way, if my synth accepts "freq" and "amp" and "wobble", I can call concatTuples [(1 :: I "wobble"), (440 :: I "freq")] and expect to get either (1 :: I "wobble", 440 :: I "freq") or the swap of that.

That is not possible: concatTuples has one type parameter, params, and in the call to concatTuples, it can just stand for one partiular type.

The values you want to pass in and out in your example are of different type (namely I "wobble" and ( I "wobble", I "freq")). This cannot work.

My impression is that that you want to do something in a very unidiomatic way for Haskell and/or vivid, and maybe someone who knows vidid better can point out to you how to approach your actual goal, i.e. how to go about coding this that you don’t end up wanting to concatenate tuples.

Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139
  • Aha! That's helpful. Could I instead write a function `joinTwoTuples :: params -> params' -> params''`, where each of those three type variables was subject to the same constraints (so there would be 6 instead of 2 constraints)? And then could I fold a list of tuples using `joinTwoTuples`? – Jeffrey Benjamin Brown Sep 03 '20 at 15:14
  • My question might amount to whether that list of tuples can be represented. I just tried the naive way, and GHCI was unconvinced: Evaluating `[(1::I "float"),(2::I "amp")]` elicits the error `Couldn't match type ‘"amp"’ with ‘"float"’`. If I can join two tuples but can't join a list of them, I'm sunk. – Jeffrey Benjamin Brown Sep 03 '20 at 15:21