I’ve begun working on a music-related ruby project, and recently I’ve
been pondering the idea of a music composition environment somewhat
similar to cm, clm and friends for lisp, but in ruby instead.
Today I was thinking about how ruby blocks make constructing
domain-specific languages a snap. I’ve been reading Lisp as a Second
Language at http://www.ircam.fr/equipes/repmus/LispSecondLanguage/, a
Lisp tutorial for musicians, and was considering using a list-based
structure for storing music. I sorta envisioned the language as
looking something like:
clef do
measure(“4/4”) do
c d e f g a b c
chord do
c e g
end
end
end
In this example, a-g are methods returning note objects.
What I’d like is to have that return a list like so:
[“c”, “d”, “e”, “f”, “g”, “a”, “b”, “c”, [“c”, “e”, “g”]]
But, as it stands, each expression is evaluated and discarded. I’d
like to avoid having to include actual lists in the blocks; while
arbitrary ruby expressions would be possible, I’m aiming to hide much
of that from the casual user.
So, my question. Is there any way to achieve something like that using
blocks? Is there any way to have yield do something other than run the
given code?
Assuming not, I was wondering how practical/horrible it would be to
have yield accept an optional block which, when given one parameter,
sets that parameter to the value of every top-level expression run
within the block? So, for instance, if I wanted to evaluate all of the
above expressions and concatenate their results into a list, I could
do:
score = []
yield { |x| score += [x] }
Of course, I realize that probably breaks the principle of least
surprise in a few instances, as many would expect the contents of a
block to be executed and only the result of all code returned, but in
my rather humble and untrained opinion, this would be useful for
implementing domain-specific languages.
Or is there a better way to accomplish what I’m trying?