In article <87brt5wz83.fsf / ethereal.dyndns.org>,
Nolan J. Darilek <nolan_d / bigfoot.com> wrote:
>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"]]
>

I did a little more work on this; now I don't have to pass in a list to 
the notes from within the chord block:

module Music

  def a(list=@tmp_notes)
    list << "a"
  end

  def b(list=@tmp_notes)
    list << "b"
  end

  def c(list=@tmp_notes)
    list << "c"
  end

  def d(list=@tmp_notes)
    list << "d"
  end
  
  def e(list=@tmp_notes)
    list << "e"
  end
  
  def f(list=@tmp_notes)
    list << "f"
  end
  
  def g(list=@tmp_notes)
    list << "g"
  end

  def clef
    puts "...In Clef..."
    yield
    @notes = @tmp_notes 
    @notes << @chord_notes unless @chord_notes.empty?
    return @notes
  end

  def measure(time)
    puts "...In Measure..."
    yield  
  end

  def chord
    #make a local copy of @tmp_notes
    tmpnotes = @tmp_notes.dup
    @tmp_notes.clear
    puts "...In Chord..."
    yield @chord_notes
    @chord_notes = @tmp_notes
    #restore @tmp_notes
    @tmp_notes = tmpnotes
  end

  def notes
    @notes
  end
end

class Song
  include Music
  def initialize
    @notes = []
    @chord_notes=[]
    @tmp_notes = []
  end
end

class MySong < Song
  include Music
  def initialize
    super
    clef do
      measure("4/4") do
        c; d; e; f; g; a; b; c;
        chord do
          c; d; g; 
        end
      end
    end
  end
end

mysong = MySong.new
p mysong.notes


So that gets rid of some of the ugliness that the user of your music 
language has to know (you want them to not have to know Ruby at all), but 
the super is still there.

Perhaps you could do something like this instead of subclassing Song, you 
could do (untested):

Defined by you:
class Song
  include Music
  def initialize
    @notes = []
    @chord_notes=[]
    @tmp_notes = []
    yield
  end
end


Defined by user in a different file:

my_song= Song.new {
    clef do
        measure("4/4") do
          c; d; e; f; g; a; b; c;
          chord do
            c; d; g; 
          end
       end
    end
}

The advantage being that you can eliminate the need for the user to have 
to define a class and call super.  You could even add more methods to 
either Song or Music so that you could do:

my_song.play

....but I'm not sure that will work; the block being passed to Song.new has 
to know about all of the methods we're using in it (a-f refer to 
@temp_list which doesn't have a context in this block).  There's probably 
some way of doing this...

Phil