
Languages vary in power. Like
Paul Graham, let's take a language of intermediate power and call it
Blub. Now someone who only speaks Blub can look at
less powerful languages and see that they're less powerful. But if they look at
more powerful languages, they won't think of them as more powerful - such language are going to look simply weird, as it is often very hard to see the power without actually being fluent in them. Even if you are shown a few examples where some language beats your favourite Blub, you're more likely to think something along the lines of "who would actually need such feature" or "well, it's just some syntactic sugar", not "omg, I've been coding Blub all my life, I have been so damn wrong". I'm not going to criticize such thinking, in many cases
weird is just weird, not really more powerful.
Oh by the way, while Paul Graham noticed this paradox,
he is also its victim - thinking that his favourite language (Common Lisp) is the most powerful language ever, so when it doesn't have some feature (object-oriented programming), then by definition such feature is "just weird", and not really powerful. Of course we all know better ;-)
Now when you really get those features of
a more powerful language, you can sometimes program better even in
Blub.
Why am I talking about that ? Because we can move one step forward and consider Ruby our new Blub. It is pretty much the most powerful general purpose language right now, but it still doesn't taze a few nice features natively :-) Fortunately we can try to understand and emulate them.
Multiple dispatch - Think about addition. Addition can easily be a method (like in Ruby or Smalltalk). You send one object a message (+, other_object). So far so good. Now how about adding Complex class ? Complex's method + must know how to add Complex numbers and normal Float numbers. But how can we tell Float what to do about Complex arguments ? Float#+ is already taken and doesn't have a clue about Complex numbers. We need to do some ugly hacks like redefining +. Or some more complex example - pattern matching. Regexp matches String. We want to implement pattern Parser that matches String. And make Regexp match IOStream. It's ugly without multiple dispatch.
Of course Ruby is not a Blub and we can easily make defgeneric ;-)
def defgeneric(name, class0, *classes, &block)
old_method = begin class0.instance_method(name) rescue nil end
class0.send(:define_method, name) {|*args|
if classes.zip(args).all?{|c,a| a.is_a? c}
block.call(self, *args)
elsif old_method
old_method.bind(self).call(*args)
else
method_missing(name, *args)
end
}
end
defgeneric(:+, Complex, Object) {|a,b|
a + Complex.new(b)
}
defgeneric(:+, Complex, Complex) {|a,b|
Complex.new(a.re + b.re, a.im + b.im)
}
defgeneric(:+, Float, Complex) {|a,b|
Complex.new(a) + b
}
Now Ruby uses a trick with coerce for handling +, but if you ever need your own generics, here they are :-)
Higher-order functions - everyone knows how to use first-order functions (those that operate only on normal non-functional objects). Most people know how to use second-order functions (those that operate on first-order functions) like
map or
each. But is it useful to go any higher ? It turns out that it is.
Imagine a container. It contains second-order methods like
each,
each_with_index,
each_pair,
each_by_depth_first_search, and two dozens other
each_whatever. It also contains other second-order functions like
map,
filter,
all?,
any?,
sort_by etc. Ever wanted
map_with_index ? How about
all_pairs? ? I sure did. In Ruby 1.9 you can use
Enumerator::Enumerable for that !
["a", "b", "c"].enum_with_index.map{|a,i| "#{i}: #{a}"} # ["0: a", "1: b", "2: c"]
Now if you look at this example long enough, you can see that it's an extremely elegant use of third-order functions :-)
Pattern matching - languages like Objective Caml do a lot of pattern matching. Pattern matching is basically a thing that
verifies that given value matches given pattern and at the same time it
deconstructs the value into pieces according to that pattern. For example this function is Objective Caml matches its argument to one of two patterns - either [] pattern (empty list) or hd::tl pattern (a non-empty list - bind hd to head of the list, and tl to its tail).
let rec sum = function
| [] -> 0
| hd::tl -> hd + (sum tl)
Or take a look at Perl/Ruby's regular expressions - they deconstruct string into $1, $2 etc. Now it's the second feature - deconstruction - that gives pattern matching all its power. So why do most programming languages lack such feature ? My guess is that it's really difficult to make patterns that deconstruct your object extensible - and verification alone isn't really all that useful. Oh yeah, and we sometimes want to build such patterns at run-time. In Objective Caml we cannot do that - we need some way to tell the language whether hd means "match contents of hd" or "match anything and put into hd". Now what if we were able to say ?:
def sum(x)
case x
when []
0
when [_{hd}, *_{tl}]
hd + sum(tl)
end
end
Useful ? Somewhat. How about this one ?
if xml_node =~ xmlp(_{name}, {:color=>_{c}}, /Hello, (\S+)/)
print "A #{c}-colored #{name} greets #{$1} !"
end
Now as far as I can tell nobody actually implemented that, but I get a feeling that using Binding.of_caller, Parse::Tree and something like defgeneric it should be possible.
Macros - well,
you can do that too :-)