The Artima Developer Community
Sponsored Link

Weblogs Forum
Make, Mailing Lists, and Ruby

8 replies on 1 page. Most recent reply: Mar 17, 2006 7:03 AM by Eric Armstrong

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 8 replies on 1 page
Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Make, Mailing Lists, and Ruby (View in Weblogs)
Posted: Mar 17, 2006 7:03 AM
Reply to this message Reply
Summary
When I found Martin Fowler's article on the benefits of Rake (the Ruby version of Make), I immediately became committed to learning Ruby. I needed to write a mailing list program, too. Ruby turns out to be ideal for both.
Advertisement

Rake and Rant -- A Better Make and Ant

Talk about pain reduction! Rake eliminates the syntax woes of makefiles and Ant. It lets you add automatic dependency rules for file types the original implementation may not have considered, and it puts the power of the programming language completely at your disposal--so it becomes possible to code conditionally-executed tasks, task loops, and other things that are difficult to do with current build languages.

Notes:

  • Martin's excellent article on Rake is at http://www.martinfowler.com/articles/rake.html
  • Rant is another implementation of the Rake syntax. It focuses on performance and reliabiltiy. But virtually all of the how-to-use-it documentation is for Rake.

With Rake, you write something that looks very much like a makefile--only in reality it's Ruby. So you do simple makefile things, or you can do complex Ruby things. It's your choice. You have the convenience of standard makefile processing with syntax like this:

# task X depends on tasks Y and Z
task :X => [:Y, :Z] do  
  ...code for task X...
end

But since the code is in reality a Ruby script, you can use the expressive power of the language to do more creative manipulations (which Folwer talks about in his article). In this code, for example, :X defines a symbol named X, and the task method (invoked without parentheses) produces a method call that looks much like a makefile entry.

So I became highly motivated to take a deeper look at Ruby. I had taken a first look several years ago. At the time, I saw a variety of cryptic Perlisms that tended to put me off, like $_. (How self-documenting is that?) Like Perl, there were multiple ways to do things. And there was operator overloading, all of which tend to make code less readable.

I wasn't a big fan of operator overloading, mostly because of the potential readability problems. I mean, it's hard enough to read code. But if you can't even count on the operators doing what you think they're doing, how can you make any sense of what you read? But then it occured to me => might be overloaded to implement the syntax needed for a build file.

As it turns out, => is defined as a hash operator. So in this case, it's mapping a target symbol to its dependencies so Rake can do things in the right sequence. So => isn't really overloaded. But thinking that it might be changed my mind about operator overloading.

In his article, Fowler concludes that an internal domain specific language really makes sense for builds, In the same vein, I conclude that operator-overloading makes a lot of sense when it's used to define an internal domain-specific language. To quote Martin's point more fully:

All three build languages share another characteristic - they are all examples of a Domain Specific Language (DSL). However they are different kinds of DSL. In the terminology I've used before:

  • make is an external DSL using a custom syntax
  • ant (and nant) is an external DSL using an XML based syntax
  • rake is an internal DSL using Ruby.

The fact that rake is an internal DSL for a general purpose language is a very important difference between it and the other two. It essentially allows me to use the full power of ruby any time I need it, at the cost of having to do a few odd looking things to ensure the rake scripts are valid ruby. Since ruby is an unobtrusive language, there's not much in the way of syntactic oddities. Furthermore since ruby is a full blown language, I don't need to drop out of the DSL to do interesting things - which has been a regular frustration using make and ant. Indeed I've come to view that a build language is really ideally suited to an internal DSL because you do need that full language power just often enough to make it worthwhile - and you don't get many non-programmers writing build scripts.

Ruby's syntactic freedom and operator-overloading combine to define a relatively clean build language. I hadn't thought of operator overloading as a language-construction tool before. But in that context, it makes a lot of sense. It's a given that things take on new meanings in a different language, whether you're talking about computer languages or natural languages.

By now, too, the cryptic constructs had become more familiar, so I didn't mind them as much. That creates an internal conflict, of course. I like to say that "Human adaptability is no excuse for a poor design." But I've adapted! And now that I'm used to it, I do like the short little syntax pf $_ compared to a longer construct like #{line}. I even read the underscore as "line"! So I'm conflicted between ease of writing and ease of reading. Sigh. But I think I'll learn to live with myself--mostly because coding in Ruby is turning out to be a lot of fun.

Ruby -- Coding can be Fun

As I went deeper, I began to appreciate Ruby much more. I had long missed C's ability to send a function pointer as an argument. In Ruby, you do that with a /closure/ (a code segment in braces that can take arguments), when I saw how you execute one inside of a method that it has been passed to.

I'm still trying to fully grok the concept of closures and find the best way to explain it. For nwo, here's what I've got: Think "C-style function pointer (or anonyomous class) passed to an iterator, with an adapter interface that lets the receiver call the 'doit' method in the function". Only instead of coding all that stuff by hand, you use a few tiny pieces of syntax to define the function: {|param| ...code...}. I mean, dang. Talk about productivity. Talk about ease of programming.

You want to process a collection? No need to code an iterator. Call "each" and pass it the code you want: myList.each {|x| ...do something with x...}. (Note the implied contract. The "each" method is defined to invoke the closure with an argument. That's not exactly obvious, at first glance. Most discussions of closures don't seem to mention that contract, which may tend to make closures more of a mystery than then need to be. (On the other hand, it may be harder to understand what's really going on than to simply treat it as magic.)

Implement some other some other methods that take closures, as well, and make them part of the standard--methods like collect and find. Then you pass a closure to the find that method that identifies the kind of thing you want: myList.find {|x| ...return true when x is a sports car or motorcyle...}.

But as good as closures are, don't stop there. Make the language dynamic, so you can iterate over pretty much anything. So you can iterate over the parameters defined in a Struct, for example, and pass a function to them to print them out--and make it super simple to get to the name of the property, so you can print that as well.

Speaking of Struct, that's a nice little 'class' that doesn't have any methods. Just properties that are always accessed with getters and setters--but use the same syntax whether or not the methods are present. Then, if you decide to add a setter method later to log changes, you can do it without changing the code everywhere the property is referenced. (I think that's the way it works, anyway.)

Then add Module--a superclass of class that can't be instantiated, but which can easily be included into a class you're writing (aka "mix-ins"). The methods don't have to be declared static. They already are, because they're in a Module. That was a Perlism worth adopting.

But wait, there's more. Not only can you add to a class you're writing by including a Module, you can truly "extend" an existing class by adding stuff to it. When you do that, you're not making a copy with a new name, you're adding to the existing class. So if there's a feature you want in String, you can add it. Nice. And you can make the additions to other code that has been using String, without having to redeclare every variable in town.

And by all means build in regular-expression string processing. My one quibble: The examples I saw had syntax that seemed backwards to me. They had /regularExpression/ =~ string to see of there is a match, rather than string =~ /regularExpression/. But it turns out that both coding styles are possible. That's basically they way it should be. If A = B, then B = A. Similarly, if /RE/ =~ A, then A =~ /RE/. A match is a match is a match.

One feature that seemed mysterious and possibly inadvisable at first glance was parallel assignment: a, b = 1, 2, which assigns 1 to a and 2 to b. (Pardon me if I get syntax wrong. I'm a concept guy. The compiler corrects me all the time.) What was the value of parallel assignment, I wondered? Then I saw this in the explanation of the yield statement, in Ruby in a Nutshell:

The expression passed to yield is assigned to the block's argument(s). Parallel assignment is performed when multiple expressions are passed.

In other words, parallel assignment is the internal glue that takes the collection of arguments passed to the yield method and binds them to the collection of parameters expected by the block. Pretty darn cool. And powerful.

Note: That seems a lot like closures, to me. But I'm told that blocks and closures are basically different things. Someday, I'm going to understand that better. When I do, I'm going to write more.

There's also a powerful case statement, of the kind I haven't seen since the Icon language, of Snobol lineage. Like Ruby, it had powerful string processing and built-in regular expression processing. It also had the same kind of case statement--one where you could put an expression at the top of the statement and compare it to arbitrary expressions in the when-clauses. So can say "when testObject.color == blue", and "when testObject.length > 40, and things like that--all in the same case statement. It's not something you need a lot, but when you do need it, you can say exactly what you mean.

There's lots of good stuff in the library, too. There's a terrific, easy to use XML parser (REXML), web-enabling packages for HTTP, mail, and CGI. A package for generating HTML. Stuff for database access and unit testing. The list goes on.

Then there's the RAILs framework. That one is exciting if only because it pretty much eliminates configuration files. Like Beans, RAILs depends on coding conventions. You follow the conventions, and the framework uses reflection to do all of the wiring for you. Very nice.

Then there are features I haven't even begun to use, like generating code inside a program and then executing it--lambda functions and Lisp-like stuff like that. That's one benefit of having a symbol-definition construct: You can define variable names and method names dynamically, and then use them. That sort of thing is going to be powerful, when I finally manage to wrap my head around it. So I get to have fun today, and even greater power tomorrow. Very enticing.

The Small Stuff

Ruby has the things you expect in an OO language, like threading, exceptions, and exception handlers. But it also has a raft of smaller features that just plain make life easier. Sometimes, it's the accumulation of small advantages that paves the way to productivity, rather than the grand concepts. Ruby has both. Here's a smattering of the goodnesses:

  • Adjacent strings automatically concatenate. No need to code "+" or "&" or what have you to put them together.

  • Inline strings that can include newlines, or "here" documents. You start one with <<<, and specify an end-tag--say XYZ. That gives you <<<XYZ. The string begins on the next line, which makes it easy to line up your text. The string ends when XYZ is found at the beginning of a subsequent line. But it gets even better than that. The string starts on the next line, even if other syntax elements exist on the initial line. So the code for sending a message can look like this:

    smtp.sendmail(<<<BODY, from, to)
    Here is the text of the message.
    Here is line two.
    BODY
    

    Note that the body of the message starts on the second line. How cool is that? Maybe it's odd. But I liked it. Of course, the text all has to start in the first column so, in practice, here-documents will be much more useful when defining constants--like when you're writing usage messages, for example. For uses like that, here-documents are invaluable.

  • Retry. Get an exception. Fix the problem in the rescue clause. Issue "retry" to start the method over again.

  • BEGIN/END segments for stuff you want to at the start or end of the program. Simple. Easy.

  • gets. If a file was specified on the command line, reads that. Otherwise, reads standard input. Puts the value in the standard location ($_). Not even one line of code--one word of code. Easy. Simple.

  • Read all lines of a file into memory with array = File::readlines(path)

  • String functions like center, include, split, slice, squeeze, strip. Plus regular expression processing that's built into the language: /...regular expression here.../.

  • -p and -n command line options. Like Perl, the interpreter iterates over the contents of a file and executes your code on each line. It then either automatically prints the line (-p), or doesn't print unless you say so (-n). Those come in handy for little jobs from time to time. It's nice to have one language for that kind of thing that you can also use for larger stuff--like the mailing list program.

Building a Mailing List Processor

The rubber meets the road when you sit down to build something. That's when you begin to answer the question, "how much work is it going to require to get stuff done?" When you're looking at a long list of backlogged projects, that becomes a pretty important question.

The answer, in Ruby, keeps turning out to be "not a heck of a lot". Reading mailing addresses out of a file: A few lines of code. Sending an SMTP message: A few more lines. Processing arguments: A couple more. Reading the message from a file: A one-liner.

The last problem to solve: Reading named parameters from a configuration file, so I can specify values like the from-address in one place. I haven't quite found the solution yet, but I'm sure it's short. It should be as simple as reading a Struct from a file. I haven't quite put the pieces together yet, but I know the answer is there.

If it weren't for the learning curve, I'd be done by now. Reading books, finding examples, understanding how things work--I've spent a little while doing that. But the code is essentially trivial. That's the way it should be! It gives me the happy feeling that the time invested in acquiring understanding will pay off handsomely in the future.

This post has gotten too long already, so I'll save the code snippets for another time. But I had to share my delight with Ruby and Rake. As always, the delight will last until I find even better tools--but it might be quite a while before that happens.


Andrew Johnson

Posts: 39
Nickname: jandrew
Registered: Mar, 2004

Re: Make, Mailing Lists, and Ruby Posted: Mar 17, 2006 12:07 PM
Reply to this message Reply
> But then I noticed that => is defined as the exception-assignment operator in Ruby

That is one of its roles in Ruby, it is also a key => value pair
separator for hashes / parameter lists.


> They syntax seems backwards to me. So you code /regularExpression/ =~ string to see of there is a match,
> rather than string =~ /regularExpression/. But I'm guessing I'll learn to live with that.

Actually, you can have both:
/regexp/ =~ "string"
"string" =~ /regexp/

as well as regexp.match(str) and str.match(regexp)

cheers,
andrew

Morel Xavier

Posts: 73
Nickname: masklinn
Registered: Sep, 2005

Re: Make, Mailing Lists, and Ruby Posted: Mar 17, 2006 2:51 PM
Reply to this message Reply
> <p>As I went deeper, I began to appreciate Ruby much more.
> I really began to understand closures (a code segment in
> braces that can take arguments), when I saw how you
> execute one inside of a method that it has been passed to.
> Think "C-style function pointer (or anonyomous class)
> passed to an iterator, with an adapter interface that lets
> the receiver call the 'doit' method in the function".
> Only instead of coding all that stuff by hand, you use a
> few tiny pieces of syntax to define the function: {|param|
> ...code...}. Then in the called routine, you use
> yield(arg) to invoke it. I mean, dang. Talk about
> productivity. Talk about ease of programming.</p>
> <p>You want to process a collection? No need to code an
> iterator. Call "each" and pass it the code you
> want: myList.each {|x| ...do something with x...}. (Note
> the implied contract. The "each" method is
> defined to invoke yield(arg). That's not exactly obvious,
> at first glance. Most discussions of closures don't seem
> to mention the yield() method, which tends to make
> closures more of a mystery than then need to be, imo.)</p>

Ouch...

Eric, you're mixing two extremely different things here and that's not good at all. I think you got somewhat confused between two things.

First, the Closure, which is a concept. A closure is a piece of code (usually wrapped in a function-like object) that carries around part of it's definition context. This means that at any time the code wrapped inside the closure can refer to what was defined where and when the closure was defined without having to explicitely pass the various data around.
Closures are not language-specific, they're mathematical concept at their core.

Then, the Block. This is the syntax, this is Ruby.
A block is an anonymous function that implements the concept of closure, nothing more nothing less (note that without blocks ruby doesn't have functions, it only has methods and ruby methods don't generate closures)

Yield isn't anywhere in the concept of closure, it's a part of Ruby's Block syntax:

First and foremost, there are two ways to define a block (aka a Ruby function):

{ |*args| code } [/code
for the inline definition

[code]do |*args|
&nbsp;&nbsp;&nbsp;&nbsp;code
end

for the "blocky" version.

They are equivalent and can be swapped, you can even use the "inline" version as a block and the "blocky" version inline (but the syntaxes have been created for readability purposes, so don't).
(Note: truth is that the two syntaxes aren't perfectly equivalent, check the Pickaxe for the details. They usually don't matter though)

Then, there are 3 ways to actually use blocks: the very magic, the slightly magic and the nearly magic-less.

Let's start with the nearly magic-less:
You can't use blocks as-is. To use a block you have to transform it into a Proc object. This can be done very simply by passing a block to the Proc constructor:
>> myblock = Proc.new {|val| p val}
This block simply prints it's argument, I could also have written it as
>> myblock = Proc.new do |val|
?> p val
?> end

To call a Proc object, simply use it's "call" method and pass the block's arguments in:
>> myblock.call 3
3
=> nil # because the block simply prints something, it doesn't actually return anything here
>> myblock.call("something")
"something"
=> nil
>> myblock.call myblock # woot
#<Proc:0x05010c60@(irb):12>
=> nil
>> myblock.call # nil argument
nil
=> nil


Nothing really magic is it? Not really sexy either, but it helps understanding how it all works.
(note: Ruby also offers a method called "proc" that behaves as "Proc.new": proc {block} is equivalent to Proc.new {block})

The second level is some slight Ruby magic:

When you create a method, you can prefix the last argument with "&":
def mymethod(foo, bar, &magic)
# code
end

If you do that and call the method with a block, then the block will be automagically transformed to a Proc object that will be bound to your magic argument:
>> def mymethod(foo, &someblock)
?> p foo
?> someblock.call
?> "It works!"
?> end
=> nil
>> a = 6
=> 6
>> mymethod(3) do
?> p a
?> end
3
6
=> "It works!"

Ruby magics the block into an argument, which means that you don't have to bother with something like
>> myproc = proc do
?> # plenty of operations
?> end
=> #<Proc:0x04fc0620@(irb):36>
>> mymethod(3, myproc)

(BEWARE: the code above DOES NOT WORK, &arg is truly magic, you can't explicitely bind it to an argument as I did here, you'd have to redefine mymethod to something like "def mymethod(foo, someblock)" for it to work)
(but if you did redefine it that way, it would work perfectly)

Much sexier, a slight touch of magic for the argument but it's mostly self explanatory and quite clear.

Notice that I haven't said anything about yield yet.

Yield is involved in black magic, the 3rd syntax for using blocks, the Invisible Blocks syntax:
>> def mymethod(foo)
?> p 3
?> yield "argument"
?> "It also works
?> end
=> nil
>> mymethod(3) do |arg|
?> p arg
?> end
3
"argument"
=> "It also works"

What happens here?

Well, let's say that Ruby magics the block (the Proc object, in fact) away and aliases Proc#call to "yield".
This means that when you use "yield" Ruby passes your stuff to the "call" method of the block you bound to your method (ouch, i'm getty very unclear here).

Notice that this syntax completely break if you do not pass a block to the method, which is why you also get a nifty "block_given?" method, which returns true if a block was passed to the method, and false otherwise, so you can do error handling or different stuff when no block is passed to your method.

Long story short, yield is part of Ruby's syntax and has nothing whatsoever to do with the concept of closures

> <p>But don't stop there. Make the language dynamic, so you
> can iterate over pretty much anything. So you can iterate
> over the parameters defined in a Struct, for example, and
> pass a function to them to print them out--and make it
> super simple to get to the name of the property, so you
> can print that as well.</p>


> <p>Speaking of Struct, that's a nice little 'class' that
> doesn't have any methods. Just properties that are always
> accessed with getters and setters--but use the same syntax
> whether or not the methods are present. So that if you
> decide to add a setter method later to log changes, you
> can do it without changing the code everywhere the
> property is referenced. (I think that's the way it works,
> anyway.)</p>
> <p>Then add Module--a superclass of class that can't be
> instantiated, but which can easily be included into a
> class you're writing ("mix-ins"). The methods
> don't have to be declared static. They already are,
> because they're in a Module. That was a Perlism worth
> adopting.</p>
> <p>But wait, there's more. Not only can you add to a class
> you're writing by including a Module, you can truly
> "extend" an existing class by adding stuff to
> it. When you do that, you're not making a copy with a new
> name, you're adding to the existing class. So if there's a
> feature you want in String, you can add it. Nice. And you
> can make the additions to other code that has been using
> String, without having to redeclare every variable in
> town.</p>
> <p>And by all means build in regular-expression string
> processing. My one quibble: They syntax seems backwards to
> me. So you code /regularExpression/ =~ string to see of
> there is a match, rather than string =~
> /regularExpression/. But I'm guessing I'll learn to live
> with that. And like other features, I wouldn't be
> surprised if there turned out to be a darned good reason
> for it.</p>
> <p>One feature that seemed mysterious and possibly
> inadvisable at first glance was parallel assignment: a, b
> = 1, 2, which assigns 1 to a and 2 to b. (Pardon me if I
> get syntax wrong. I'm a concept guy. The compiler corrects
> me all the time.) What was the value of parallel
> assignment, I wondered? Then I saw this in the explanation
> of the yield statement, in Ruby in a Nutshell:</p>
> <blockquote>
> The expression passed to yield is assigned to the block's
> argument(s).
> Parallel assignment is performed when multiple expressions
> are passed.</blockquote>
> <p>In other words, parallel assignment is the internal
> glue that takes the collection
> of arguments passed to the yield method and binds them to
> the collection of parameters expected by the block. Pretty
> darn cool. And powerful.</p>
> <p>There's lots of good stuff in the library, too. There's
> a terrific, easy to use XML parser (REXML), web-enabling
> packages for HTTP, mail, and CGI. Stuff for database
> access and unit testing. The list goes on.</p>
> <p>Then there's the RAILs framework. That one is exciting
> if only because it pretty much eliminates configuration
> files. Like Beans, RAILs depends on coding conventions.
> You follow the conventions, and the framework uses
> reflection to do all of the wiring for you. Very nice.</p>
> <p>Then there are features I haven't even begun to use,
> like generating code inside a program and then executing
> it--lambda functions and Lisp-like stuff like that. That's
> one benefit of having a symbol-definition construct: You
> can define variable names and method names dynamically,
> and then use them.</p>
> <p>That sort of thing is going to be powerful, when I
> finally manage to wrap my head around it. So I get to have
> fun today, and even greater power tomorrow. Very
> enticing.</p>
> </div>
> <div class="section" id="the-small-stuff">
> <h1><a name="the-small-stuff">The Small Stuff</a></h1>
> <p>Ruby has the things you expect in an OO language, like
> threading, exceptions, and exception handlers. But it also
> has a raft of smaller features that just plain make life
> easier. Sometimes, it's the accumulation of small
> advantages that paves the way to productivity, rather than
> the grand concepts. Ruby has both. Here's a smattering of
> the goodnesses:</p>
> <blockquote>
> <ul>
> <li><p class="first">Adjacent strings automatically
> concatenate. No need to code "+" or
> "&"
> or what have you to put them together.</p>
> </li>
> <li><p class="first">Inline strings that can include
> newlines, or "here" documents. You start
> one with <<<, and specify an end-tag--say XYZ.
> That gives you <<<XYZ.
> The string begins on the next line, which makes it easy to
> line
> up your text. The string ends when XYZ is found at the
> beginning of a subsequent
> line. But it gets even better than that. The string starts
> on the next
> line, <em>even if other syntax elements exist on the
> initial line</em>. So the
> code for sending a message can look like this:</p>
> <pre class="literal-block">
> smtp.sendmail(<<<BODY, from, to)
> Here is the text of the message.
> Here is line two.
> BODY
> </pre>
> <p>Note that the body of the message starts on the second
> line. How cool is that?
> Maybe it's odd. But I liked it. Of course, the text all
> has to start in the
> first column, so in practice this technique will be much
> more useful when
> defining constants. But for usage messages and the like,
> constants like that
> are invaluable.</p>
> </li>
> <li><p class="first">Retry. Get an exception. Fix the
> problem in the rescue clause. Issue "retry"
> to start the method over again.</p>
> </li>
> <li><p class="first">BEGIN/END segments for stuff you want
> to at the start or end of the program.
> Simple. Easy.</p>
> </li>
> <li><p class="first">gets. If a file was specified on the
> command line, reads that. Otherwise,
> reads standard input. Puts the value in the standard
> location ($_).
> Not even one line of code--one <em>word</em> of code.
> Easy. Simple.</p>
> </li>
> <li><p class="first">Lots of string functions like center,
> include, split, slice, squeeze,
> strip. Plus regular expression processing that's built
> into the language:
> /...regular expression here.../.</p>
> </li>
> <li><p class="first">-p and -n command line options. Like
> Perl, the interpreter iterates over
> the contents of a file and executes your code on each
> line. It then
> either automatically prints the line (-p), or doesn't
> print unless you say
> so (-n). Those come in handy for little jobs from time to
> time. It's nice
> to have one language for that kind of thing that you can
> also use for larger
> stuff--like the mailing list program.</p>
> </li>
> </ul>
> </blockquote>
> </div>
> <div class="section"
> id="building-a-mailing-list-processor">
> <h1><a name="building-a-mailing-list-processor">Building a
> Mailing List Processor</a></h1>
> <p>The rubber meets the road when you sit down to build
> something. That's when you begin to answer the question,
> "how much work is it going to require to get stuff
> done?" When you're looking at a long list of
> backlogged projects, that becomes a pretty important
> question.</p>
> <p>The answer, in Ruby, keeps turning out to be "not
> a heck of a lot". Reading mailing addresses out of a
> file: A few lines of code. Sending an SMTP message: A few
> more lines. Processing arguments: A couple more. Reading
> the message from a file: A one-liner.</p>
> <p>The last problem to solve: Reading named parameters
> from a configuration file, so I can specify a from address
> and the like in one place. I haven't quite found the
> solution yet, but I'm sure it's short. It should be as
> simple as reading a Struct from a file. I haven't quite
> put the pieces together yet, but I know the answer is
> there.</p>
> <p>If it weren't for the learning curve, I'd be done by
> now. Reading books, finding examples, understanding how
> things work--I've spent a little while doing that. But the
> code is essentially trivial. That's the way it should be!
> It gives me the happy feeling that the time invested in
> acquiring understanding will pay off handsomely in the
> future.</p>
> <p>This post has gotten too long already, so I'll save the
> code snippets for another time.
> But I had to share my delight with Ruby and Rake. As
> always, the delight will last until I find even better
> tools--but it might be quite a while before that
> happens.</p>
> </div>

Morel Xavier

Posts: 73
Nickname: masklinn
Registered: Sep, 2005

Re: Make, Mailing Lists, and Ruby Posted: Mar 17, 2006 2:54 PM
Reply to this message Reply
Oh lord, sorry for the mess of the previous post, I hit "post message" instead of preview, i'm really sorry.

I'll just go away and sit in the corner now...

Admin Admin

Posts: 39
Nickname: admin
Registered: Jan, 2002

Re: Make, Mailing Lists, and Ruby Posted: Mar 18, 2006 9:44 AM
Reply to this message Reply
> Oh lord, sorry for the mess of the previous post, I hit
> "post message" instead of preview, i'm really sorry.
>
> I'll just go away and sit in the corner now...

Hi Morel,

In our new forums architecture (coming soon) you'll be able to go back and edit something after posting. Humans sometimes do things like hit post instead of preview by mistake, so the software should expect that and provide options when it happens.

Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Re: Make, Mailing Lists, and Ruby Posted: Mar 18, 2006 9:45 AM
Reply to this message Reply
> > Oh lord, sorry for the mess of the previous post, I hit
> > "post message" instead of preview, i'm really sorry.
> >
> > I'll just go away and sit in the corner now...
>
> Hi Morel,
>
> In our new forums architecture (coming soon) you'll be
> able to go back and edit something after posting. Humans
> sometimes do things like hit post instead of preview by
> mistake, so the software should expect that and provide
> options when it happens.

For example, I didn't realize I was logged in as admin when I made this previous post, so because I didn't sign it, you would have no idea who posted it. How's that for an illustration of the concept?

Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Re: Make, Mailing Lists, and Ruby Posted: Mar 18, 2006 6:02 PM
Reply to this message Reply
Andrew wrote:
> => is also a key/value pair separator for hashes and
> parameter lists.
>
Thanks. I did a bit more reading and realized that. I'll
update the text.

> The syntax seems backwards to me. So you code
> /regularExpression/ =~ string to see of there is a match,
>
> Actually, you can have both:
> /regexp/ =~ "string"
> "string" =~ /regexp/
>
> as well as regexp.match(str) and str.match(regexp)
>
Way cool! Thanks.

Of course, all those different ways to do things makes
programs harder to read when they're written by others.
Here I am conflicted again--because I at the same time
truly delighted to find that I can write thing the way
/I/ want to.
:_)

Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Re: Make, Mailing Lists, and Ruby Posted: Mar 18, 2006 6:11 PM
Reply to this message Reply
Morel Xavier wrote
>
> ...an extensive and truly kind explanation of the
> difference between blocks and closures--two concepts
> which I thoroughly conflated in the article
>
I can only say thanks. My take away at this point is
that a block is not a closure, and that yield is not
part of closure. I see I have to revise that section,
as well.

I wish I could say that I truly got the picture. What
kills me is that I /recall/ the closure concept from
when I was studying abstract algebra--but I have as yet
been unable to relate that concept to what's termed a
closure in programming languages. It kills me, because
I'm sure there is a reason for using that name, so
there is some basic unifying theme that I am totally,
absolutely, and completely missing. I still do not grok
it's essence, and it's killing me! (But why let a little ignorance stand in the way of advertising a great tool,
I figured.)
:_)

Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Re: Make, Mailing Lists, and Ruby Posted: Mar 18, 2006 6:12 PM
Reply to this message Reply
> Oh lord, sorry for the mess of the previous post, I hit
> "post message" instead of preview, i'm really sorry.
>
> I'll just go away and sit in the corner now...

>
Not at all! Given the length and quality of what you wrote,
it's only natural that you forget about all the stuff that was off screen below it.
:_)

Flat View: This topic has 8 replies on 1 page
Topic: The Java Posse Previous Topic   Next Topic Topic: Refactoring reminiscent of high school algebra

Sponsored Links



Google
  Web Artima.com   

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