From block to code (also: how to get the bindings of a block)

I am aware of this technique, something very similar was also described in a
very nice article by Jim Freeze on Ruby Code and Style.

The recipe is just an example as I said.
Truly, I don't see why I shouldn't use the equal signs.

Let's just say that the language makes it hard doing it. No hard feelings, I
still love Ruby :slight_smile:

We can always do things in other ways, but that's also the argument that
many javists and dotnetters use against ruby.

···

On 5/1/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Tue, 2 May 2006, chiaro scuro wrote:

> On 5/1/06, Jacob Fugal <lukfugl@gmail.com> wrote:
>>
>> On 5/1/06, chiaro scuro <kiaroskuro@gmail.com> wrote:
>> > I need to keep the equal signs.
>>
>> This is my big question; why do you need the equal signs? I see
>> nothing inherent about the "recipe" domain that makes the equal signs
>> necessary or even desirable. If you were willing to ditch them, a
>> whole slew of simpler and cleaner implementations are available.
>>
>> Jacob Fugal
>>
> I realize that would make it much easier in ruby.
> It's just that the equal signs make lots of sense to my DSL users.
(forget
> the recipes, it's just an example)
>
> Look, it's not a matter of life or death. *of course* I can do without
if
> there is no alternative.
> (eventually I did find a solution to this -although it's a cheesy one-
but I
> am not saying yet :wink:
>
> It just bothers me that I cannot access that context if I need to, and
in
> this case I do.

it would be tough to convince me that people wouldn't find this syntax
natural:

   harp:~ > cat a.rb
   class Recipe < Hash
     attr 'name'
     def initialize name, &b
       @name = name
       instance_eval &b
     end
     def method_missing m, *a, &b
       m = m.to_s.delete '='
       self[m] = a.size > 1 ? a : a.shift
     end
   end
   def recipe(*a, &b) Recipe.new(*a, &b) end

   r =
     recipe "breakfast" do
       eggs 2, :big
       bacon 3
     end

   p r

   harp:~ > ruby a.rb

   {"bacon"=>3, "eggs"=>[2, :big]}

for example, write down a recipe and see if you use the '=' sign :wink:

regards.

-a
--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

Not sure about the ATs. even if I don't mind them that much... if I unfocus
my eyes they look like bullet points :wink:

I have got this one working.. see if you can guess how..

recipe "eggs and bacon" do
  eggs = [2,:big]
  bacon = 3
the end

I posted my solution here:
http://liquiddevelopment.blogspot.com/2006/04/way-of-meta-part-iv-hijacking-local.html

···

On 5/2/06, Ross Bamford <rossrt@roscopeco.co.uk> wrote:

On Tue, 2006-05-02 at 05:11 +0900, chiaro scuro wrote:
> On 5/1/06, Jacob Fugal <lukfugl@gmail.com> wrote:
> >
> > On 5/1/06, chiaro scuro <kiaroskuro@gmail.com> wrote:
> > > I need to keep the equal signs.
> >
> > This is my big question; why do you need the equal signs? I see
> > nothing inherent about the "recipe" domain that makes the equal signs
> > necessary or even desirable. If you were willing to ditch them, a
> > whole slew of simpler and cleaner implementations are available.
> >
> I realize that would make it much easier in ruby.
> It's just that the equal signs make lots of sense to my DSL users.
(forget
> the recipes, it's just an example)

In return for keeping the equals signs, would the users let you get away
with a few 'at' symbols?

def recipe(name, &blk)
  o = Object.new
  o.instance_eval(&blk)
  o.instance_variables.inject({}) do |h,s|
    h[s[1..-1]] = o.instance_variable_get(s)
    h
  end
end
# => nil

recipe "eggs and bacon" do
  @eggs = [2,:big]
  @bacon = 3
end
# => {"bacon"=>3, "eggs"=>[2, :big]}

(Though I'd really recommend most of the other ideas from this thread
over this).

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

In a word: yuck. :slight_smile:

Why don't you preprocess the source and add the needed @s?

James Edward Gray II

···

On May 1, 2006, at 7:27 PM, chiaro scuro wrote:

Not sure about the ATs. even if I don't mind them that much... if I unfocus
my eyes they look like bullet points :wink:

I have got this one working.. see if you can guess how..

recipe "eggs and bacon" do
eggs = [2,:big]
bacon = 3
the end

Without looking, I'd guess def the; binding; end ? (Quick look, done
with alias, same difference I guess).

It's a clever solution but it'd make the DSL sound a bit whimsical for
my taste. Plus, I'd be either forgetting to put 'the' or resenting that
I had to... :slight_smile:

Also, I think it might give unexpected results in some situations, e.g:

some_other_var = :oops

shopping_list "english breakfast" do
  tomatoes = 2, :green
  sausages = 3
  eggs = 2, :big
  bacon = 4
the end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
the end

Gives output:

to make english breakfast you should buy:
* 2 green tomatoes
* 3 sausages
* oops some_other_var
* 4 bacon
* 2 big eggs

to make banana milkshake you should buy:
* 2 bananas
* 1 milk
* oops some_other_var

Granted in a DSL this is less likely to occur, but guaranteed to leave
users scratching their heads if (when?) it does. Generally I prefer if I
can preserve normal, predictable behaviour for those times when you (or
some power user of your system) might need it.

···

On Tue, 2006-05-02 at 09:27 +0900, chiaro scuro wrote:

Not sure about the ATs. even if I don't mind them that much... if I unfocus
my eyes they look like bullet points :wink:

I have got this one working.. see if you can guess how..

recipe "eggs and bacon" do
  eggs = [2,:big]
  bacon = 3
the end

I posted my solution here:
http://liquiddevelopment.blogspot.com/2006/04/way-of-meta-part-iv-hijacking-local.html

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

If you really want the = look you can capture the binding with the
set_trace_func utility. This has drawbacks right now as there is no
"clean" way to share set_trace_func cooperatively w/o some code (some
small hacking could easily add this though -- sample bellow). So this
will get rid of the ugly "the" but you will need to be careful that
the Kernel stuff is executed before other things that might possibly
want to use the old style tracer:

# Could be cleaned up quite a bit but it should work.
module Kernel
  tracers =
  legacy_tracer = nil

  set_trace_func lambda {|*args|
    legacy_tracer.call(*args) if legacy_tracer
    tracers.dup.each {|tracer| tracer.call(*args)} unless args[3] == :trace
  }

  define_method(:set_trace_func) {|proc|
    legacy_tracer = proc
    nil
  }

  # Not thread safe but easy to make so.
  define_method(:trace) {|tracer, block|
    tracers << tracer
    result = block.call
    # lambda {} == lambda {} work around just in case.
    tracers.reject! {|x| x.object_id == tracer.object_id}
    result
  }
end

def recipe(name)
  recipe_binding = nil
  trace(
        lambda {|event, file, line, id, binding, classname|
          recipe_binding = binding unless id
        },
        lambda {yield})
  ingrediants = eval("local_variables", recipe_binding)
  puts "To make #{name} you need:"
  ingrediants.each {|i|
    info = eval(i, recipe_binding)
    info = info.join(' ') if Array === info
    puts " #{info} #{i}"
  }
end

recipe "eggs and bacon" do
  eggs = 2, :big # Note that ruby has a nicer syntax than explicit here.
  bacon = 3
end

I get this output:

To make eggs and bacon you need:
  2 big eggs
  3 bacon

Hope this helps,
Brian.

···

On 5/1/06, chiaro scuro <kiaroskuro@gmail.com> wrote:

Not sure about the ATs. even if I don't mind them that much... if I unfocus
my eyes they look like bullet points :wink:

I have got this one working.. see if you can guess how..

recipe "eggs and bacon" do
  eggs = [2,:big]
  bacon = 3
the end

> recipe "eggs and bacon" do
> eggs = [2,:big]
> bacon = 3
> the end

In a word: yuck. :slight_smile:

:slight_smile:

seriously, can you think of another 'last word' that fits well at the end
of a block without being annoying? (in either of the do..end and {..} cases)

Why don't you preprocess the source and add the needed @s?

I think I'll go for a ParseTree based solution in the end. That might
include either what you suggest or the artificial injection of a binding
comman. I would have liked a nicer way though..

···

On 5/2/06, James Edward Gray II <james@grayproductions.net> wrote:

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

True, I need to check variables before and after calling the block and work
out the differences.
And it is a bit whimsical.. can you think of a better word than "the" for a
DSL?

···

On 5/2/06, Ross Bamford <rossrt@roscopeco.co.uk> wrote:

On Tue, 2006-05-02 at 09:27 +0900, chiaro scuro wrote:
> Not sure about the ATs. even if I don't mind them that much... if I
unfocus
> my eyes they look like bullet points :wink:
>
> I have got this one working.. see if you can guess how..
>
> recipe "eggs and bacon" do
> eggs = [2,:big]
> bacon = 3
> the end
>
> I posted my solution here:
>
Liquid Development: The Way of Meta - Part IV - Hijacking Local Variables in DSLs
>

Without looking, I'd guess def the; binding; end ? (Quick look, done
with alias, same difference I guess).

It's a clever solution but it'd make the DSL sound a bit whimsical for
my taste. Plus, I'd be either forgetting to put 'the' or resenting that
I had to... :slight_smile:

Also, I think it might give unexpected results in some situations, e.g:

some_other_var = :oops

shopping_list "english breakfast" do
  tomatoes = 2, :green
  sausages = 3
  eggs = 2, :big
  bacon = 4
the end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
the end

Gives output:

to make english breakfast you should buy:
* 2 green tomatoes
* 3 sausages
* oops some_other_var
* 4 bacon
* 2 big eggs

to make banana milkshake you should buy:
* 2 bananas
* 1 milk
* oops some_other_var

Granted in a DSL this is less likely to occur, but guaranteed to leave
users scratching their heads if (when?) it does. Generally I prefer if I
can preserve normal, predictable behaviour for those times when you (or
some power user of your system) might need it.

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

Ross Bamford <rossrt@roscopeco.co.uk> writes:

shopping_list "english breakfast" do
  tomatoes = 2, :green
  sausages = 3
  eggs = 2, :big
  bacon = 4
the end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
the end

Just toying around:

recipe "english breakfast" do |take|
  take(2).green.tomatoes
  take(3).sausages
  take(2).big.eggs
  take(4).slices.of.bacon
end

···

Ross Bamford - rosco@roscopeco.REMOVE.co.uk

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

And it is a bit whimsical.. can you think of a better word than "the" for a
DSL?

Hmm, not really. I guess if you really have to have the superfluous
(from the user point of view) word in there, you might add a new layer
of madness and do:

require 'facet/binding'

def shopping_list recipe = nil
  return Binding.of_caller {|b|b} unless recipe
  shopping_binding = yield
  ingredients = {}
  eval("local_variables" , shopping_binding).each do |var|
    ingredients[var] = eval "#{var}" , shopping_binding
  end
  report recipe, ingredients
end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
shopping_list end

which is longer, but less whimsical IMHO.

(Btw another problem with this approach is that it plays havoc with
vim-ruby's auto indentation :frowning: )

···

On Tue, 2006-05-02 at 09:52 +0900, chiaro scuro wrote:

On 5/2/06, Ross Bamford <rossrt@roscopeco.co.uk> wrote:
>
> On Tue, 2006-05-02 at 09:27 +0900, chiaro scuro wrote:
> > Not sure about the ATs. even if I don't mind them that much... if I
> unfocus
> > my eyes they look like bullet points :wink:
> >
> > I have got this one working.. see if you can guess how..
> >
> > recipe "eggs and bacon" do
> > eggs = [2,:big]
> > bacon = 3
> > the end
> >
> > I posted my solution here:
> >
> Liquid Development: The Way of Meta - Part IV - Hijacking Local Variables in DSLs
> >
>
> Without looking, I'd guess def the; binding; end ? (Quick look, done
> with alias, same difference I guess).
>
> It's a clever solution but it'd make the DSL sound a bit whimsical for
> my taste. Plus, I'd be either forgetting to put 'the' or resenting that
> I had to... :slight_smile:
>
> Also, I think it might give unexpected results in some situations, e.g:
>
> some_other_var = :oops
>
> shopping_list "english breakfast" do
> tomatoes = 2, :green
> sausages = 3
> eggs = 2, :big
> bacon = 4
> the end
>
> shopping_list "banana milkshake" do
> milk = 1
> bananas = 2
> the end
>
> Gives output:
>
> to make english breakfast you should buy:
> * 2 green tomatoes
> * 3 sausages
> * oops some_other_var
> * 4 bacon
> * 2 big eggs
>
> to make banana milkshake you should buy:
> * 2 bananas
> * 1 milk
> * oops some_other_var
>
> Granted in a DSL this is less likely to occur, but guaranteed to leave
> users scratching their heads if (when?) it does. Generally I prefer if I
> can preserve normal, predictable behaviour for those times when you (or
> some power user of your system) might need it.
>
> --
> Ross Bamford - rosco@roscopeco.REMOVE.co.uk
>
>
>

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

--
This email has been verified as Virus free
Virus Protection and more available at http://www.plus.net

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

I like it :slight_smile:

it feels very 'natural' in the recipe case.

you could also go for a:

  take 2, :green, :tomatoes

···

On 5/2/06, Christian Neukirchen <chneukirchen@gmail.com> wrote:

Ross Bamford <rossrt@roscopeco.co.uk> writes:

> shopping_list "english breakfast" do
> tomatoes = 2, :green
> sausages = 3
> eggs = 2, :big
> bacon = 4
> the end
>
> shopping_list "banana milkshake" do
> milk = 1
> bananas = 2
> the end

Just toying around:

recipe "english breakfast" do |take|
  take(2).green.tomatoes
  take(3).sausages
  take(2).big.eggs
  take(4).slices.of.bacon
end

> Ross Bamford - rosco@roscopeco.REMOVE.co.uk
--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
shopping_list end

That's the best variation of the binding trick, in my opinion.

(Btw another problem with this approach is that it plays havoc with
vim-ruby's auto indentation :frowning: )

Yeah, all the warnings (code smells) are there. If I went ahead with a solution like this, I would feel like I was being irresponsible. I think it's time to find a new approach.

James Edward Gray II

···

On May 1, 2006, at 8:25 PM, Ross Bamford wrote:

"chiaro scuro" <kiaroskuro@gmail.com> writes:

I like it :slight_smile:

it feels very 'natural' in the recipe case.

you could also go for a:

take 2, :green, :tomatoes

Ideally

   take 2.green.tomatoes

but I wanted to avoid needing to change any core classes.
Hm, if you suppress warnings:

  take 2, green tomatoes

···

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Yeah, couldn't agree more. The whole thing seems kind of clunky and
inelegant to me. It's error prone, likely to fail silently or with some
really strange errors. I do enjoy finding how far I can push things but
if this were me and the code was for users, I'd avoid like the plague.

···

On Tue, 2006-05-02 at 11:38 +0900, James Edward Gray II wrote:

On May 1, 2006, at 8:25 PM, Ross Bamford wrote:
> (Btw another problem with this approach is that it plays havoc with
> vim-ruby's auto indentation :frowning: )

Yeah, all the warnings (code smells) are there. If I went ahead with
a solution like this, I would feel like I was being irresponsible. I
think it's time to find a new approach.

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

RiffRaff has driven the last meta-nail in my coffin with this inventive
solution:
http://riffraff.blogsome.com/2006/05/02/metaprogramming-breakfast/

···

On 5/2/06, Christian Neukirchen <chneukirchen@gmail.com> wrote:

"chiaro scuro" <kiaroskuro@gmail.com> writes:

> I like it :slight_smile:
>
> it feels very 'natural' in the recipe case.
>
> you could also go for a:
>
> take 2, :green, :tomatoes

Ideally

   take 2.green.tomatoes

but I wanted to avoid needing to change any core classes.
Hm, if you suppress warnings:

  take 2, green tomatoes

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/