Monday, January 23, 2006

Re: Re: Re: Ruby Array class sucks

I have been meaning to respond to this post on the factor blog for awhile. Factor has grown a lot, and this blog tracks the development of it. Factor is the result of Forth and Lisp having babies. But the post I'm interested in doesn't have so much to do with factor but with ruby. It is in response to another post (URL at the factor site) about a complaint with Ruby's Array class. The complaint centers mainly around the Array object having far more methods than it should and quite a few that don't belong there. Slava's (factor creator) response suggests that the methods do deserve to be there and from a functional standpoint most of them make plenty of sense.

For a number of the methods in Array, I do agree with slava that they should be present. For instance, the authors complaint about Array.new being able to preset an array to a specific value is silly to me, I often want some sort of initial value to work from so I would imagine many other people are in my boat.

Around Array.transpose, I start to disagree though. transpose takes an Array and treats it like a matrix. Slava's response is:

It might surprise the author to learn that mathematically, a matrix is a two-dimensional array. In Factor, you can perform mathematical operations on vectors, such as vector addition, dot product, and so on.


Ok, so basicaly the argument is, a matrix resembles an array in some way and factor allows these sort of manipulations, thus ruby should. Well ok, let's say, yes, a matrix is a two-dimensional array, that's nice. But what does that make an Array then? An Array is also a two-dimensional array? That sounds a bit circular. Why should a method in Array depend on Array being thought of as a specific type of object. If we want to do matrix work, I suggest abstracting a matrix type, even if it's as simple as Matrix = Array. But really, it sounds like transpose should be in some sort of Matrix object.

Array.rassoc also seems a bit out of place to me. Slava says:

Yes, and there's nothing wrong with that. Association lists, while they suffer from linear lookup time, are occasionally useful where you want to have a mapping together with a certain intrinsic order. For example, Factor's generic word code takes the hashtable of methods defined on a generic, converts this to an association list, then sorts the association list according to the partial ordering of classes. Finally, the sorted association list is used to generate code.

Again, the argument of "This is ok because factor does it" does not quite fly with me. Now, when I write code and I'm not quite sure what data type I really want, but I need some sort of key->value association, I will often just use a list or array type to start out with, abstracted into some interface so when I decide what I finally need I can simply change the backend. Now, is this a good reason to put a function that treats a list like a map inside a list object? In my opinion, No.

In my opinion, I would use some sort of generic sequence object that has various methods that act on arrays and lists and other sequence objects, allowing you to perform some of these manipulations. In most languages, I would probably go as far to even make something like rassoc just be a plain function, no need to make it part of an object. But I have a feeling a lot of Ruby programmers don't feel content unless they have methods in some class.

This Array object seems to be similar to various classes in Python. Back in the day, Python developers could add methods to classes without any sort of vote. This is why the Python standard lib has lots of random modules that one would not normally think should be in a standard library, and also why Python has been making such an effort to clean up a lot of the standard classes and provide a more consistent interface to them.

Sorry this post does not actually contain anything on erlang. My next one will, I promise. Feel free to flame me in response to my Ruby thoughts, just be civil.