Eric Kidd: Why Ruby is an Acceptable Lisp
randomhacks.net
randomhacks.net
Of course this can be overcome - he mentions two projects which could turn Ruby from most of a Lisp into 100% of a Lisp with direct access to the parse tree. But those are not used right now in every day Ruby programming.
cache(key_name, :ttl => 30.seconds, :dependent => ModelClass) do
model.expensive_operation
end
and have that cache expire whenever any instance of ModelClass is updated.With macros, every call to cache() can be expanded into:
ModelClass.register_expiration_callback(keyname)
do_cache(...)
Without macros, there's no way to catch deeply nested cache calls. I fake this by scanning the source for calls to cache() on startup right now.FWIW, I believe that Ruby or (especially) Smalltalk-style syntax for method calls and closures covers such a large set of the things macros are used for that their complexity cost (esp when building tools) outweighs their benefit.
I completely agree that there are a large number of uses for macros that "SmallTalk style" syntax completely replace. It turns out that a lot of things don't really require you to run code at parse time. Take anaphoric-if for example, which is classically implemented as a macro; all you really need is the ability to manipulate the local variables of a closure from the outside. Parse-time execution is not required.
http://www-formal.stanford.edu/jmc/recursive/recursive.html
Does that sound like 'Ruby'?
I don't think so.
So def my_fun(a,b,c) is different from {|a,b,c|...}
I find it very confusing and as a matter of fact, I can not remember what I have to instantiate when to pass around (there is a Proc object, but does it refer to a fn or a block? And what would be the name for the other thing?).
Just a reminder that shortness is not the only criterion - it also has to be intuitive/logical.
Of those three, Procs are the only ones that are actually objects (i.e., can be assigned to variables and can receive messages).
puts_method = method(:puts)
def get_block(&block) block end
my_block = get_block { puts_method.call('hello!') }
my_block.call #=> hello! puts puts_method.class #=> Method
puts my_block.class #=> Proc
Method and Proc are types; blocks are a syntactic construct. Method.ancestors #=> [Method, Object, Kernel] def nuts(a, b, c)
end
A method can take a block (a block is not an object, it's syntax): nuts { |a, b, c| ... }
You can capture a block as a Proc: foo = proc { |a, b, c| ... }
foo.call(1, 2, 3)
def nuts(&proc)
proc.call(1, 2, 3)
end
nuts { |a, b, c| ... }
There is also a Method object: method(:nuts).call(1, 2, 3)
And you can turn a method into a Proc: method(:nuts).to_proc.call(1, 2, 3)
Why does it makes sense to have these as different things? Because we want to use return in the blocks: def nuts
@vars.each do |var|
case var
when String
# Returns the method, not the block
return "Test"
end
end
end def nuts(a, b)
end
foo = Proc.new { |a, b| }
nuts(1, 2, 3) # => ArgumentError: wrong number of arguments (3 for 2)
foo.call(1, 2, 3) # works fine
We also have lambda, which is a Proc that works (almost) as a method: http://samdanielson.com/2007/3/19/proc-new-vs-lambda-in-rubyAlso, what will the return do if the block has been defined outside of the method, will it also return from the method?
Anyway, thanks for the clarification. I guess to_proc was what I had been looking for back then when I ran into the problem.