From: Gavin Sinclair Date: 2002-09-05T09:56:39+09:00 Subject: Re: Ruby aesthetics > On Wed, 2002-09-04 at 02:56, Gavin Sinclair wrote: > >> My frame-of-reference for good language design is Ruby. So compared >> to Ruby, I would add: >> - list comprehensions >> - calling procs would look like real functions, not arrays! >> e.g. id = proc { |x| x }; id(12) # not id[12] >> - first-class functions (someone please correct me here) >> - def within def >> >> The first one is already discussed elsewhere in this thread, and I >> don't want to sound like a zealot just by mentioning it here. The >> others probably have issues; I've not thought it through. >> [From Kent:] > While calling procs as foo(x,y) is consistent with other languages, one > must remember IMO that one of Ruby's strong points is its > "expressiveness". Procs -aren't- functions in the strictist sense, but > arguments -are- arrays. :-) Hense, foo[x,y]. If it looks like a function, and acts like a function, ... (only half-serious) Args to procs are arrays, but the foo[x,y] "syntax" merely derives from the definition of Proc.[]. That's a good thing, I guess, because methods are more flexible than syntax. foo(x,y) would require syntax-awareness, which is an odd fit, since procs can be defined at runtime. Oh, hang on! So can methods, and method discovery occurs at runtime as well. > Why do we need first-class functions? Ruby is really a collection of > base methods with built-in extensibility (either via extension of > existing methods or the creation of new ones.) I must admit, I'm not entirely sure what I'm talking about here ;) Whatever other languages achieve with first-class funtions, Ruby achieves handsomely with blocks, closures, iterators - whatever you want to call them. However, *sometimes*, when I try things "functionally", working with procs can get a bit ugly. > Def within def? Why not simply: > > def foo() > # do something > end > > def bar() > #do somehing > x = foo > # do something with x > end > > Collections of short methods (which could be methods calling other > methods) appears more easily maintainable IMO. This was a point of > discussion awhile ago on the list here. Why not def-within-def? It can make it clear that one method is merely a helper for another, while making use of lexical scoping. > Regards, > Kent Starr I'll come clean. I'm studying Scheme (only incidentally - some of you have read "The Structure and Interpretation of Computer Programs", haven't you?). Scheme is moderately difficult (for me) to read, write and (especdially) debug, but the book is great, and the language is instructional. So sometimes I try things out in Ruby using the same paradigms. Forgive me for getting a bit long here, but I'll provide an example. Exercise 1.7 calls for a definition of (sqrt x) using Newton's approximation. It's just a modification of what's in the book using a different "good-enough?" definition. Here's the answer: (define (abs x) (if (> x 0) x (- x))) (define (average a b) (/ (+ a b) 2)) (define (sqrt x) (define (sqrt-iter g1 g2) (if (good-enough? g1 g2) g2 (sqrt-iter g2 (improve g2)))) (define (good-enough? g1 g2) (< (/ (abs (- g1 g2)) g2) 0.000001)) (define (improve guess) (average guess (/ x guess))) (sqrt-iter 1.0 2.0)) -- The point is that (abs x) and (average a b) are good all-round useful functions, whereas (sqrt-iter g1 g2), (good-enough? g1 g2) and (improve guess) all: - are relevant only to (sqrt x) - can use the "x" parameter in (sqrt x) So if they'd been defined outside (sqrt x), they would: - be a bit unclear about why they exist (they're hardly general) - have to take an extra parameter When I implemented this in Ruby (there was a bug due to a misplaced bracket), I wrote: def sqrt(x) good_enough = lambda { |g1,g2| ((g1 - g2).abs / g2) < .001 } improve = lambda { |guess| average(guess, x/guess) } sqrt_iter = lambda { |g1,g2| (good_enough[g1,g2]) ? g2 : sqrt_iter[g2, improve[g2]] } sqrt_iter[1.0, 2.0] end # Uses a definition for Enumerable.sum not shown here def average(*args) (args.sum / args.length) unless args.length.zero? end -- The point of all this? 1. When I use named lambdas, I'm wondering what the difference is between it and a function/method - so why can't I def-within-def? 2. This line of coding is for experimentation only; I'm not implying that Ruby should accept all manner of other paradigms, and I realise that probably all the abstraction achieved in the Scheme example would be well expressed using OO techniques in Ruby. 3. At the same time, however, it's healthy to consider what limits there are on Ruby and whether they should be there. 4. If anyone's interested in really good books, check out SICP (mitpress.mit.edu/sicp/full-text/book/book.html). Cheers, Gavin