(sorry to post so much… hope this is all still interesting!)
A quick look at SuperCollider’s MIDIFunc gives a good example of an API giving the wrong affordance, and thus making composition of control hard:
An example of it’s use (from SC’s help):
notes = Array.newClear(128); // array has one slot per possible MIDI note
on = MIDIFunc.noteOn({ |veloc, num, chan, src|
notes[num] = Synth(\default, [\freq, num.midicps,
\amp, veloc * 0.00315]);
});
off = MIDIFunc.noteOff({ |veloc, num, chan, src|
notes[num].release;
});
Notice that the user of MIDIFunc has to do the matching of noteOff to noteOn. To compose with this interface (imagine we have two such users), the composition has to do a lot of work, and somewhat invasively.
Had the API been defined to be used like this, it would have been much easier:
on = MIDIFunc.note({ |velocOn, num, chan, src|
var note = Synth(\default, [\freq, num.midicps,
\amp, velocOn * 0.00315]);
// and return the function to call when the note is off
{ |velocOff| note.release; }
});
This is both less code for the client, the client no longer relies on a global(-ish) array, and is much easier to compose.