The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Ruby's rich Array API

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Florian Frank

Posts: 48
Nickname: ffrank
Registered: Dec, 2005

Florian Frank is a humanoid life-form, living on the third planet of the solar system.
Ruby's rich Array API Posted: Dec 21, 2005 7:47 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Florian Frank.
Original Post: Ruby's rich Array API
Feed Title: The Rubylution
Feed URL: http://rubylution.ping.de/xml/rss/feed.xml
Feed Description: The Rubylution is a weblog (mainly) about Ruby Programming.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Florian Frank
Latest Posts From The Rubylution

Advertisement

There's an ongoing discussion in the Blogsphere about Ruby's Array API, that was started by Martin Fowler.

Elliotte Rusty Harold obviously didn't like it as much as Martin did. His article is full of misunderstandings and it seems, that the only research he did, was skimming and scanning the Ruby Array documentation, trying to find something to dislike. (That would be anything, that's not like it is in Java.) I mean, if he truly had questions about Ruby, why didn't he ask on the Ruby Talk Mailing List? Rubyists are nice people, he would have gotten lots of answers in no time.

He seemed to dislike the Array API, because it offers a lot of useful methods, that one would expect in several different data structures instead of a single one. From the Ruby user's point of view a richer API isn't bad: Blending queues, stacks, sets, arrays and lists in one class, only makes its instances more useful. You don't have to use Array#to_a on an array, that is used as a stack. But it sure pays off to have it available, if you want to output it for example:

puts *stack

(The splat operator * is calling #to_a to do its work.) Or to iterate over its elements

for elm in stack
end

which requires the #each method.

This is a pragmatic approach: You don't have to find a way to convert a stack into an array first.

One problem with this is that inheritance is more difficult, because a class that extends Array would have to support all of the Array instance methods. Inheriting from Array is a newbie error in Ruby - it's much better to delegate to an encapsulated Array instance instead, in order to restrict the supported protocol. There are several ways to do this in Ruby: manually, method_missing, forwardable.rb, delegate.rb, and you can easily define your own delegate meta programming method.

Another problem is that offering a full duck typing protocol compatible to that of Array for your own classes is a lot of work. It would of course be possible to offer a mixin, that only expects a few methods to be in your class, and then offers a default implementation of all of Arrays instance methods based on those. This wouldn't be as efficient in every case, but would make it at least more easy. But these difficulty only occurs, if you really want to write your own "quacks like an array" type of classes from scratch.

In Ruby it's quite easy to achieve the effect of restricting the protocol an object will understand by means of meta programming. It's possible to extract a class that only offers a subset of the original protocol:

Stack = Array.extract([
  :last,
  :push,
  :pop,
  :size,
  :clear,
  :inspect,
  :to_s
])

Now the new born Stack's instances don't respond to all the array methods anymore, while Stack is still based on Array's implementation.

s = Stack.new
s.push 1
s.push 2
s.push 3
s       # => [1, 2, 3]
s.last  # => 3
s.pop   # => 3
s       # => [1, 2]

To get a set of operations with names, that are usually used in computer science text books, renaming would be cool as well:

Queue = Array.extract([
  :first,
  :push,
  :shift,
  :clear,
  :size,
  :inspect,
  :to_s
]).rename(
  :push   =>  :enqueue,
  :shift  =>  :dequeue
)

Aliasing and defining new methods (without reopening the class) comes in handy as well:

List = Array.extract([
  :first,
  :last,
  :push,
  :insert,
  :delete,
  :delete_at,
  :[],
  :[]=,
  :size,
  :each,
  :length,
  :inspect,
  :to_s
], [ Object, Enumerable ]).rename(
  :push   =>  :add
).alias(
  :[]     =>  :get,
  :[]=    =>  :put
).define(:sum) { |*start|
  inject(start[0] || 0) { |s,x| s + x }
}

The List objects will still respond to all Object and Enumerable instance methods (second parameter to extract). You can mimic Java's get and put methods as well:

l = List.new
l.add 1
l.get 0       # => 1
l.put 0, 23   # => 23
l.get 0       # => 23

To create a tool set that supports this is quite easy: Just duping the Array class object and reconfigure it's protocol. It can be done in 42 lines of Ruby. Here's the implementation and some additional examples. Is it possible to do that in 42 lines of Java, too?

Read: Ruby's rich Array API

Topic: Don't look for ideas where you can't find them Previous Topic   Next Topic Topic: Rubygems plugin for Typo

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use