Undocumented syntax for "{; Comprehension" in SuperCollider

SuperCollider has had the great feature of implementing List Comprehension for decades.

But taking a look around the source code, I found a strange syntax that I don’t remember being documented anywhere. And it’s not an “exotic” curiosity, it’s a sophisticated feature.

Have you ever seen List Comprehensions using {; instead of {:? These aren’t your typical list comprehensions, but they’ve got some similar vibes. I will call it “Monad Comprehension”, because that’s a way it could be interpreted in Haskell.

Look:

  • Normal List Comprehension syntax: {:

    {: x*2, x <- (2..6)}.all  // Gives you: [4, 6, 8, 10, 12]
    
    • This one gives you control over what gets yielded and when.

      {; x*2, x <- (2..6)}  // Immediately outputs the first result: 2
      
  • Use {; when you only want to output specific results or need to control outputs based on certain conditions, present of past states, etc.

It doesn’t exist as List Comprehension in Haskell, but can be achieved as a Monad Comprehension. See:

import Control.Monad (guard)

doubleIfGreaterThanFive :: Int -> Maybe Int
doubleIfGreaterThanFive x = do
    guard (x > 5)  -- If x is not greater than 5, the computation outputs Nothing
    return (x * 2)  -- Otherwise, return Just the doubled value 

processList :: [Int] -> [Maybe Int]
processList xs = do
    x <- xs            
    return (doubleIfGreaterThanFive x) 

main :: IO ()
main = print $ processList [3, 6, 8, 2, 10, 1]

Another Example in SuperCollider:

r = Routine { loop { {; if (x*2 > 10) { yield (x*2)}, x <- (2..6) } } };

r.nextN(30); 
-> a Routine
-> [ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 ]

It deserves proper documentation.

2 Likes