Reverse superclass envy and bad mental pointers

Posted by ungraspiness Fri, 31 Aug 2007 05:06:00 GMT

I’ve ranted previously about Python and made promises of future posts about why Ruby is so much cooler. It really is and the topic of this article covers one of the common idioms used by most Ruby folks who may have taken a shot at their own domain specific language in Ruby. The following is one of the really useful idioms but apparently not a very well understood or well explained one for Ruby coders.

To many Ruby folks this is the vague-but-really-really-cool-idiom that is
class << self; self; end;

This is used a lot in the Rails framework. You shouldn’t have to look very deep into the Rails source code to find it. As an example I’ll guess that most Ruby coders are familiar with Why’s example for Dwemthy’s Array that explains the idiom.

Here’s the relevant bit of example code:
class Creature
  def self.metaclass; class << self; self; end; end

  ... more code for meta-programming goes here ...

  traits :life, :strength, :charisma, :weapon
end

This then allows the definition of other classes derived from the Creature class such as these for our example:

class GreenMonster < Creature
  traits :greenness, :scariness
  life 90
  strength 45
  charisma 23
  weapon 80
  greenness 10
  scariness 12
end

class RedMonster < Creature
  traits :redness, :loudness
  life 75
  strength 50
  charisma 90
  weapon 65
  redness 20
  loudness 33
end

It seems that when it comes to understanding how this idiom works most of the confusion has to do with misnaming the key feature used in the Creature class, specifically the ‘metaclass’ in the code above. It’s usually called a meta-class(as in our example), shadow-class or class-object-instance-singleton , yikes!. (Adding in the additional popular belief that Ruby classes behave like objects can make this even more difficult to grasp. They do behave like objects but they technically are not like most objects in Ruby). Technically the ‘metaclass’ is a singleton for each derived class we have but this is not as important (and probably best ignored for now) as defining what the ‘metaclass’ really is in terms of object-oriented meaning.

Here I offer my attempt at another simple explanation:

The idiomatic line of code is merely a hack to obtain access to the sub/derived/child class (correct OO nomenclature). In Ruby, classes have access to their parent class via the ‘superclass’ method. The equivalent method to go the other direction i.e. ‘subclass’ would be very useful but because there is no such method the following hack is required. The scope that is accessible to the code between the class << self … end block has access to the derived class scope and so ‘self’ is returned from the block which is then returned from the parent class method ‘self.metaclass’. ‘self’ is the sub-class derived from Creature. In our example it’s either the GreenMonster or RedMonster class.

There is no magic in this idiom when this little expletive is made clear but the way it’s usually explained and the popular naming seem to be the main source of confusion. Once you look at this for what it is, there’s nothing magic except for the popular names applied to it. Maybe we should start using the more informative/explicit sub/child class naming and leave out the explanation of classes behaving as objects when explaining this useful idiom ?



or maybe that’s just me in the corner, in the spotlight…

Posted in  | Tags ,

Recursive reduction

Posted by ungraspiness Mon, 23 Jul 2007 03:39:00 GMT

The previous post was pretty grumpy about Python/Django and I was going to write a follow-up about why Ruby is a better language (unfortunately not the platform’s performance just yet but that is soon to change with YARV).

For this post to appear more fair about Ruby/Python than the last let’s look at one reason where these don’t come close to Lisp. This is something mentioned less often(but still important) when comparing Lisp with other languages which usually focus on macros and s-exprs.

Here’s a python snippet that was shared with me not so long ago:

def myreduce(f, t, list):
  size = len(list)
  if size == 1:
    return f(list[0], t)
  else:
    return myreduce(f, f(list[0], t), list[1:size])

It’s a reduce function[1] that I’m not sure the author knows is buggy. It takes a function as the first parameter, an initializer value and the list to reduce.

To test the function we could code something like this in Python:
import random
func = lambda x,y: x + y
>>> myreduce(f, 100, [1,2,3,4,5,6])
121

A first attempt at improving on this in Ruby:

def myreduce(l, t, &f)
  return yield(t, l[0]) if l[0] and l.size == 1
  myreduce( l[1..(l.size-1)], yield(t, l[0]), &f )
end

myreduce( [1,2,3,4,5,6], 100 ) { |x,y| x + y }   # returns 121

Much simpler (and terse) also using a block. Seemed to work just fine but what happens when we have a longer list to reduce, a million integers ?

myreduce( [*1..1000000], 100 ) { |x,y| x + y }

>SystemStackError: stack level too deep
>        from (irb):2:in `myreduce'
>        from (irb):3:in `myreduce'
>        from (irb):19
>        from :0

Oops, we run out of stack space ! The same problem exists in the Python version.

random_list = [random.randrange(1,1000000) for i in range(1,1000000)]
myreduce(func, 100, random_list)

> File "<stdin>", line 4, in myreduce
> RuntimeError: maximum recursion depth exceeded

We could increase the stack size but this is a hack and we would never have a large enough stack size for any practical purposes if we were to modify this code for real world use.

To have a more robust version we need to drop the recursive call and just use a loop. So the new Ruby version would look like this

def myreduce_loop(l, t, &f)
  tot = t
  for it in l
    tot = yield(tot, it)
  end
  tot
end

myreduce_loop( [*1..1000000], 100 ) { |x,y| x + y }
=> 500000500100

The Python version would be very similar. It’s obvious that the Lisp way of using recursion isn’t practical with Python or Ruby. Yes, there are ways to hack stack frames in Python and possibly in Ruby too.

In Lisp we can use tail recursion because this allows the compiler to optimize recursive calls so that the stack doesn’t blow up and performance is improved(hopefully).

Here’s the tail-recursive reduce function in Lisp.
(defun range(s e) 
  (let ((l nil))
    (do ((n s (+ n 1)))
    ((> n e) (reverse l))
      (setf l (cons n l)))))

(defun myreduce(f l &optional (i 0))
  (if (null l)
      i
      (progn 
          (if (null (cdr l)) 
              (funcall f i (car l))
              (myreduce f (cdr l) (funcall f i (car l) ))))))

(format t "~D ~%" (myreduce #'+ (range 1 1000000) 100))
500000500100

Ok so we’ll get back to why Ruby is still the next best language in the following post.




[1] Python’s built in functions include ‘reduce’. Search the page after clicking on the link.

Posted in  | Tags , , , , ,

Red vs. Blue (or don't be so blue man)

Posted by ungraspiness Thu, 12 Jul 2007 22:05:00 GMT

Pointing out the cons of a language like Java can be risky. The responses sometimes are that such attempts are based on highly subjective opinions. Let’s take byte code manipulation for example. Many Java coders find this invaluable for doing anything remotely interesting in Java. After all it’s heavily used in servlet containers and object-relational mapping tools such as JDO implementations. How is this not an invaluable feature ? Well, a Lisp hacker knows Java byte code manipulation to be a con because they can’t help but compare it with Lisp’s macros. Byte code manipulation is still a con for Java programmers too, it’s just that many of them may not be aware of why this is so. It’s really not a case of subjectivity but of awareness(or lack thereof) on the part of those that make the statement against anyone that’s able to see the cons of Java byte code manipulation (or any of Java’s other cons). Seems obvious enough and probably one of the reasons that Sun has so happily backed the current JRuby project.

This entry started after reading a somewhat biased comparison of Ruby and Python which still made some good points (or at least it made me write this !).

First off, Ruby’s meta-programming is superior to Python’s in terms of simplicity and power. Nothing new here .

Now that we have that stuff out of the way here’s another biased article with my points of view. Let’s look at some issues with Python/Django…


Snake Bites

Are the following pros or cons of Python ?
Have you ever seen a switch statement in Python ?
  result = {
    'a': lambda x: x * 5,
    'b': lambda x: x + 7,
    'c': lambda x: x - 2
  }[value](x)
Yes that’s right, it doesn’t exist and that’s the closest you can get.

How is having to define self for all class methods in Python good ?
  class Example:
    def method(self, x):
      print "method was passed ", x
Anyone new to object-oriented programming (or maybe not) will not find this intuitive at all and will be slowed down in learning the concepts. or a class method in Python ?
class thing:
  count = 0
  def __init__(self, value):
    self.value = value
    thing.count += 1
  def getval(self):
    return self.value
  def getcount(cls):
    return thing.count
  counter = classmethod(getcount)
yikes !

There are so many examples that could follow but let’s leave it at these and figure out why we have…


Too much gas

If a framework implemented in a certain language shares the same ‘DNA’ with that language in sense(e.g. Rails code borrowing from Ruby design) then this is very obvious with Django too:

  • White space: (there I said it) Python’s significant white-space is part of the reason for Django’s crappy and almost unusable template syntax.
  • Design: Unrelated (but still annoying) is Django’s broken MVC design. Django calls a controller a view, there’s an unnecessary model layer for form handling (manipulators and now forms which are really just new manipulators) and views are templates that support a funky object-oriented hierarchy similar to ASP.Net (but easier to grasp).
  • Badly used meta-programming: Decorators are used in Django to change parameter types and not in the way that you may find useful like for form handling, where converting strings to integers would be useful.
  • ORM: The Object Relational mapping features require a degree in HtHdtW(How the hell does this Work!). e.g. Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline=’Hello’) Why all the double underscores ? Tried multi-table queries recently ? or how about database transactions (they need to be defined in a view?!). SQLalchemy is supposed to transmute this lead into gold in the near future and it honestly does look like a big improvement.
  • Debugging: Trying to debug a Django application more complex than “hello world” can be really difficult. Django has no logging facility so some pretty cool folks have taken this upon themselves
  • Community: Has anyone had any success in submitting bug fixes for these ? (I don’t know anyone that has) Any sizable project I’m aware of has had to do a fork of the framework.
  • Why is Django’s version number so close to 1.0 at this point ?

This provides some valid reasons as to how language can play a role in the frameworks built using them. Language effects how programmers think about problems (more so than they realize sometimes) and this can effect what and how certain things are built with a language.

Now, to the person who first responded to the comparison article: Are most of Python’s(and as an extension Django’s) cons just highly subjective ? It seems more like we all love to call them just “preferences” and repeat the mantra of peaceful language/framework co-existence in spite of what we remain unaware of in other languages (or worse, remain in denial of).


Co-exist ?

Is it great that Ruby and Python continue to both co-exist now and for the future ? Would we want to co-exist today with Neanderthal man running down main street and vandalizing cars ? (Please ignore the obvious jokes about those with language preferences different to yours). My point is that what was important and necessary in the past may likely be a problem today. Obviously we want to move on(certain species sometimes have to become extinct or evolve) and hence one of the possible reasons for Django copying Rails in many respects as it tries to ‘evolve’ instead of becoming extinct. The same holds true for the evolution of programming languages. Certain steps are required in the process to reach the next plateau(well maybe not Lisp it kind of just showed up!). It seems like a waste of resources in duplicating such framework efforts and giving new programmers(or web programmers) the chance to learn some really bad habits can’t be good either. Why not help some new/potential programmers learn some good habits and have fun while they do so, we could send them over to hackety.org and rubyonrails.org and possibly prevent more snake bites and severe gas from too much “Bebop cola”.


Negativa

The best programmers approach programming in terms of what is possible instead of what’s not possible (obviously staying aware of a language’s limitations but not making this their primary focus). Afterall, why look at life primarily through the shades of negation instead of the eyes of opportunity ? (Do you see what I’m getting at ?). The negative approach is usually more suited to other subjects. Who really wants a mind full of caveats when it comes to a language/framework ? Obviously they all have these to a degree but isn’t less of these better than more ? How is a programming language that does not work consistently the way you would expect it too as good as one that does ?

So Python/Django are pretty cool but Ruby/Rails are much better and Lispy frameworks are best(but just not as widely accessible… ...yet). Hopefully Ruby/Rails heads even further in the direction of Lisp. It does seem very likely with cool projects like RLisp starting to take shape.


the end

Those insurance commercials with the intelligent cavemen are really funny because they may reflect some truths about the society we live in that finds them appealing.


The pros(and cons) of Ruby (or feelin’ lovey dovey)

Coming soon to this blog !



Posted in ,  | Tags , , , ,

Making class methods re-usable from modules (in Ruby)

Posted by ungraspiness Mon, 25 Jun 2007 21:56:00 GMT

module TestMod
end

def TestMod.append_features(klass)
  def klass.method
    puts "#{self}: in method" 
  end
end

class Class1
  include TestMod
end

class Class2
  include TestMod
end

Class1.method()
Class2.method()

This should work even without the authors code available.

Posted in  | Tags  | no comments

Ruby Metaprograming

Posted by ungraspiness Mon, 16 Oct 2006 02:10:00 GMT

Ola Bini’s Ruby Metaprogramming techniques is a great summary of the meta-programming techniques in common use today by many of the interesting Ruby libraries.

Namely:

  1. Use the singleton-class
  2. Write DSL’s using class-methods that rewrite subclasses
  3. Create classes and modules dynamically
  4. Use method_missing to do interesting things
  5. Dispatch on method-patterns
  6. Replacing methods
  7. Use NilClass to implement the Introduce Null Object refactoring
  8. Learn the different versions of eval
  9. Introspect on instance variables
  10. Create Procs from blocks and send them around
  11. Use binding to control your evaluations

Other good resources on this topic:

Posted in  | Tags ,  | no comments

Ruby and Lisp

Posted by ungraspiness Tue, 10 Oct 2006 22:42:00 GMT

Eric Kidd wrote a great article comparing Ruby and Lisp last year called Why Ruby is an acceptable LISP. Besides the annoyance that he calls it LISP there were many great points and counter-points from the comments. Reading everything behind the link above is advised.

The articles main points:

1. Ruby is a denser functional language than LISP Lisp.
2. Ruby gives you about 80% of what you want from macros.
3. Ruby’s libraries, community, and momentum are good

Some of his examples are not as true as they were last year but I’m sure that the author is aware of this.

So for anyone considering one over the other this is the best on the topic that I’ve seen so far.

Posted in ,  | Tags  | no comments

This tastes good...

Posted by ungraspiness Tue, 24 Jan 2006 21:56:00 GMT

...like strawberries

def parse_directories(dir)
  sub_dirs = []
  Dir.new(dir).collect do |item|
    path = (dir + '/' + item).gsub('//', '/')
    if File::directory?(path) and item[0].chr != '.'
      sub_dirs << path
    end
  end
  @directories.concat(sub_dirs)
  #Recursively find other sub dirs
  sub_dirs.each do |subdir| 
     parse_directories(subdir)
  end
end

Tags  | no comments

Try Ruby !

Posted by ungraspiness Sat, 17 Dec 2005 02:08:00 GMT

Ruby is a programming language from Japan (available at ruby-lang.org) which is revolutionizing the web. The beauty of Ruby is found in its balance between simplicity and power.

Try Ruby !

Posted in  | Tags ,  | no comments