Lambda the Ultimate

inactiveTopic Using Ruby
started 8/31/2003; 7:33:19 PM - last post 9/5/2003; 1:21:39 PM
Chris Rathman - Using Ruby  blueArrow
8/31/2003; 7:33:19 PM (reads: 11319, responses: 21)
Using Ruby
An Introduction to Ruby for Java Programmers. This a presentation that Jim Weirich is giving to introduce Ruby to those unfamiliar with the language. Pretty good at wheting the apetite, but the slides that caught my eye were the ones concerning typing:

There are a lot of choices when designing a type system

  • Strong vs Weak
    • Strong: Impossible to treat an object as an wrong type
    • Weak: Possible to use improper operations on an object
  • Static vs Dynamic
    • Static: The type of an expression is known at compile time.
    • Dynamic: The type of an expression is only known at run time.
  • Explicit vs Implicit
    • Explicit: All variables and functions are explicitly typed (also called Manifest typing)
    • Implicit: The types of variables are implicit (also called Latent typing)

Nothing earth shattering but since there's been some discussion about the usefullness of the term "dynamic typing", I thought the breakdown might be useful.
Posted to OOP by Chris Rathman on 8/31/03; 7:40:43 PM

Ehud Lamm - Re: Using Ruby  blueArrow
9/1/2003; 7:41:04 AM (reads: 1237, responses: 0)
So, how's Ruby doing these days?

Ehud Lamm - Re: Using Ruby  blueArrow
9/1/2003; 7:45:21 AM (reads: 1234, responses: 1)
lines.join.split.size

Functional Object Oriented Programming?

Or maybe it's just that in Ruby arrays take the place lists have in functional programming as the universal glue datatype (cf. Why FP Matters)?

Dominic Fox - Re: Using Ruby  blueArrow
9/1/2003; 8:26:43 AM (reads: 1219, responses: 1)

Here's one of quite a few places where I find I prefer Ruby to Python. Python's array.reverse() reverses the array in place and returns null (IIRC). You can't string array operations together - array.reverse().reverse() gives you an error, not the original value of array. I've obviously become contaminated by FP thinking, because this now seems entirely wrong to me...

Vesa Karvonen - Re: Using Ruby  blueArrow
9/1/2003; 9:13:49 AM (reads: 1208, responses: 1)
Python's array.reverse() reverses the array in place and returns null (IIRC). You can't string array operations together - array.reverse().reverse() gives you an error, not the original value of array. I've obviously become contaminated by FP thinking, because this now seems entirely wrong to me...

Side-effects must be sequenced properly. However, in most languages the order in which sub-expressions are evaluated is not completely defined (in particular, the order in which parameters are evaluated is often undefined). In such a language, the sequence in which the side-effecting procedure applications of an expression are evaluated is generally undefined. This can lead to many kinds of surprising problems. Therefore, side-effecting procedures should generally not return a useful value.

For example, consider the following snippet using a side-effecting reverse() that returns the modified array.

  a = [5, 10]
  a.reverse()[0] - a.reverse()[0]

What is the value of the second expression?

Daniel Yokomiso - Re: Using Ruby  blueArrow
9/1/2003; 9:27:48 AM (reads: 1227, responses: 0)
For example, consider the following snippet using a side-effecting reverse() that returns the modified array.

a = [5, 10] a.reverse()[0] - a.reverse()[0]

What is the value of the second expression?

If we are talking about something like Java, the second expression whould return 5. If a language leaves expression evaluation order undefined, such "expressions" should be considered errors.

BTW, Ruby defines "reverse()" to be side-effect free, so the result would be 0.

Daniel Yokomiso - Re: Using Ruby  blueArrow
9/1/2003; 9:33:37 AM (reads: 1224, responses: 0)
I thought the "Why FP Matters" paper talked about lazy-evaluated lists as the universal datatype instead of simple lists. In Ruby it would be some kind of iterator.

I always had this question: in a language with Sather-like iterators wouldn't the modularity benefits described in "Why FP Matters" be available?

Vesa Karvonen - Re: Using Ruby  blueArrow
9/1/2003; 10:05:42 AM (reads: 1195, responses: 0)
If we are talking about something like Java, the second expression whould return 5.

Right. However, this isn't the case in all (most?) imperative languages (e.g. Scheme, C, Ocaml,...). Even in Java and Python, where the order of expression evaluation is defined to be from left to right (modulo precedence and associativity), I would probably avoid relying on expression evaluation order too much.

Vesa Karvonen - Re: Using Ruby  blueArrow
9/1/2003; 10:18:17 AM (reads: 1195, responses: 0)
I always had this question: in a language with Sather-like iterators wouldn't the modularity benefits described in "Why FP Matters" be available?

Consider constructing a function that transforms a program using iterator operations to a program that uses operations on lazy lists and another function that transforms a program using operations on lazy lists to a program using iterator operations.

Patrick Logan - Re: Using Ruby  blueArrow
9/1/2003; 11:04:52 AM (reads: 1180, responses: 2)
I always had this question: in a language with Sather-like iterators wouldn't the modularity benefits described in "Why FP Matters" be available?

I think this is a good point and can be generalized to say that objects are a means to delay evaluation (among other things). The syntax may not be as desirable as implicit laziness, but the results are similar.

Vesa Karvonen - Re: Using Ruby  blueArrow
9/1/2003; 11:19:45 AM (reads: 1167, responses: 0)
By the way, since Ruby is the topic, how does Ruby define the order of evaluation of sub-expressions?

Daniel Yokomiso - Re: Using Ruby  blueArrow
9/1/2003; 11:31:14 AM (reads: 1203, responses: 0)
The syntax may be as simple as implicit lazyness the language just need to support a simple anonymous object syntax and user-defined operators (so we can use SML-like "::")


abstract class Iterator(a) {
    abstract isDone(): Boolean;
    abstract current(): a;
    abstract rest(): Iterator(a);
}

infixr :: (head: a, tail: Iterator(a)): Iterator(a) { return {isDone := false; current := head; rest := tail}; }

empty := {isDone := true; current := error; rest := error}; break := empty;

values := 1 :: 2 :: 3 :: empty; loop { print(values!) }

twice(value: a): Iterator(a) { return value :: value :: empty; }

loop { print(twice(42)!) }

find(it: Iterator(a), pred: Predicate(a)):Maybe(a) { result := nothing; loop { each := it!; if pred(each) { result = each; break; } } return result; }


Outside the iterator library, the user will just use cons (::) and empty, but even inside the library the syntax is clean.

Patrick Logan - Re: Using Ruby  blueArrow
9/1/2003; 12:02:11 PM (reads: 1152, responses: 0)
So, how's Ruby doing these days?

The way I characterized it's popularity at OSCON was Perl is to Python as Python is to Ruby. Perl is about an order of magnitude more popular than Python, and two orders more than Ruby.

Ehud Lamm - Re: Using Ruby  blueArrow
9/1/2003; 12:11:06 PM (reads: 1168, responses: 0)
You can't string array operations together - array.reverse().reverse() gives you an error, not the original value of array.

I discussed exactly this sort of thing in a little story I posted to LtU a while back.

Ehud Lamm - Re: Using Ruby  blueArrow
9/1/2003; 12:13:37 PM (reads: 1167, responses: 0)
I think this is a good point and can be generalized to say that objects are a means to delay evaluation (among other things).

And so are functins (at least if they are first class).

But perhaps we are on the verge of a discussion about procedural abstraction vs. data abstraction? Imagine the fun.

Dominic Fox - Re: Using Ruby  blueArrow
9/1/2003; 12:23:34 PM (reads: 1135, responses: 0)

I can see why it's better for side-effecting operations not to return useful values; I think that what's got into me is a preference for referential transparency where possible, such that I would simply prefer it if [1, 2, 3].reverse() were non-side-effecting. Ruby appeals to me (over Python) insofar as it tends to swing that way as well. But this is rather trivially personal.

Dominic Fox - Re: Using Ruby  blueArrow
9/1/2003; 2:02:24 PM (reads: 1133, responses: 0)
Perl is to Python as Python is to Ruby

Patrick was talking about orders of magnitude of popularity, but it struck me that there might be some other senses in which this could be argued as well. Python can be characterised as "Perl cleaned up": it tries to simplify, to lay out orthogonally (whether it really does this is perhaps debateable, but my point is just that it was one of GvR's stated intentions), to find one ("Pythonic") way to do it, and so on.

Now Ruby does not really continue in that exact direction - in fact it's seen by some detractors as lapsing back in the direction of Perl, largely I think on the superficial basis that it happens to use a couple of "line noise" operators. But Ruby no less than Python has a notion of "the Ruby way" behind it, and part of "the Ruby way" is to use concise little block functions (which incidentally are also closures) and combine them with iterators.

Ruby had yield before Python did, and Ruby's block syntax is not a wart the designer would like to do away with (which is the case with Python's lambda). In spite of Ruby's fairly thorough-going object orientation, a core Ruby idiom is the application of functions to lazily evaluated lists (well, OK, iterators). Its libraries use that idiom extensively; you need to be comfortable with it to get on with the language. So Ruby is - at times - not so much a "message passing" language as a "block passing" language. It's a kind of strong misreading, if you'll pardon the expression, of Smalltalk.

Dan Shappir - Re: Using Ruby  blueArrow
9/2/2003; 12:36:21 AM (reads: 1057, responses: 1)
Interestingly I found a lot of similarities between Ruby and JavaScript (BTW Ruby was one of the languages Sjoerd and I were looking at while building BeyondJS). The main difference is that Ruby is class-based while JavaScript is prototype-based.

Another more subtle difference is the distinction Ruby makes between methods and blocks. Both are like functions but are defined differently, have different calling conventions etc.

In JavaScript both are functions, or more precisely methods (functions are methods of a global singleton object). Also, in JavaScript functions are passed like any other type, there is no "last implicit arg", and you call them via the reference, not using a special yield instruction. I prefer the JavaScript way here.

We did encountered some warts mixing functional style with OO we had to introduce a Function.from() operator to convert methods on objects into functions. So I do agree there are advantages to the Ruby way.

I consider Ruby's use of arrays as a "universal glue datatype" a bit dangerous. JavaScript uses arrays the same way, which is why BeyondJS introduces lazy lists. The reason I see arrays as dangerous is that using them is a lot easier than constructing containers that support iterators, thus many programmers may simply use arrays disregarding potential problems. Consider the readlines function (block? method?). It would be tempting to write code that scans a file for values as:

readlines.grep(/hello/).each {|line| puts line}

But what if the file is 10GB? In BeyondJS I would write:

File.StdIn.filter(/hello/).foreach(alert);

which does the same thing. But because File.StdIn returns a lazy list, its content is never stored in memory all at the same time.

Dan Shappir - Re: Using Ruby  blueArrow
9/2/2003; 12:50:39 AM (reads: 1069, responses: 0)
Obviously Ruby can change it's internal implementation of arrays to support lazy evaluation, but that caries its own overhead. And, given Ruby's implementation of iterators, I doubt this will happen.

Oleg - Re: Using Ruby  blueArrow
9/3/2003; 12:29:31 PM (reads: 838, responses: 1)
I'd like to point out an argument why cursors (aka iterators) are inferior to enumerators -- from the point of view of resource management, and the ease of programming, to name the few reasons. Furthermore, in a language with first-class (delimited) continuations, it's possible to pure mechanically convert an enumerator to a cursor, should the latter be necessary.

SRFI-44 discussion archive http://srfi.schemers.org/srfi-44/mail-archive/maillist.html covers this topic in detail. See also http://pobox.com/~oleg/ftp/Scheme/enumerators-callcc.html

Ehud Lamm - Re: Using Ruby  blueArrow
9/3/2003; 12:37:43 PM (reads: 858, responses: 0)
By enumerators you mean for-each like procedures, right? I am more familiar with the term passive iterator.

Anyway, if this is what you mean, then clearly it is a viable option only in high-order languages (with anonynous functions, first-class functions etc.)

The iterator is a solution to a programming language problem. Most patterns are.

Oleg - Re: Using Ruby  blueArrow
9/5/2003; 1:21:39 PM (reads: 767, responses: 0)
By enumerators you mean for-each like procedures, right?

That is right. Alas, the terminology in this area is very confusing: it's quite unfortunate that C++ STL chose the term "iterator" for a passive entity that should be better called an "accessor" or a "cursor". That's why the articles mentioned earlier use the terms "enumerator" and "iteratee", or a "cursor".

The iterator is a solution to a programming language problem. Most patterns are.

Well said!