3 Posts Tagged 'Scheme'
Books are our friends
I went on a reading binge over Thanksgiving break. Read on for reviews.
Test-Driven Development By Example
First I read Test-Driven Development By Example. This is a very short book, especially given the hefty price tag. I would probably not have bought this if I'd seen it on a shelf at the store rather than online.
That said, it's a good book if you want to understand the mindset behind the whole test-driven development fad. The book is heavy on mindset and light on mechanics; it doesn't tell you how to set up an environment, how to compile and run tests, or any such thing. It just goes through some examples and explains what the programmer was thinking, and how to tackle the problem from a TDD point of view. The intended audience therefore is someone who has plenty of experience programming and wants to learn a new way to look at problem solving.
The book goes through writing some terrible code (admittedly terrible by the author) to solve a simple problem, then refactoring the code to be less and less terrible by means of tests. The writing style is engaging and casual and he describes mistakes as well as successes, which is a nice way to write a tutorial book. The alternate writing style, belting out the correct answer the first time every time, is not as enlightening, and I appreciate it in this book.
The book places a heavy emphasis on writing tests FIRST, making small incremental changes, and refactoring as you go. It also explains how TDD can be related to various OOP design patterns, which I didn't find all that helpful. And it describes some "test patterns", which I found even less helpful. But it's mostly an aesthetic objection; something about "design patterns" sits less and less well with me the more I get away from Java-like languages. The information is still good, straightforward and to the point, take it or leave it.
I have been unable to drink the TDD Kool-Aid, but I can see where it'd probably result in an improvement in some of my code for certain specific kinds of problems. I'll probably try it out again next time I have to write a little library at work.
This book is expensive and short, but the alternative for learning about TDD are half-baked blog posts, which is why I bought a book. I don't know of a better book because this is the only one I have, and I'm unsure at this point whether I really want to buy another book on this topic. But this book is highly recommended by many TDD fanatics enthusiasts, so I don't know.
The Little Schemer
Next I read The Little Schemer. The style of this book can best be described as "cute". Examples all use food, and it instructs you at various points to go make yourself a sammich (even leaving room in the book for "jelly stains"). This somehow makes the topics, which can be rather intimidating, somehow less scary. The book is very short, and not very dense (not many words on a page), but they somehow cram a lot of information into the book anyways, via the many examples. It's a very focused and methodical book.
This book goes through developing a simplified dialect of Scheme, with a focus on recursion and building up parts of the language from very simple primitives. There are some neat little things along the way, like using s-expressions to represent numbers, and building up a full set of arithmetic functions using nothing but "add 1", "subtract 1", and recursion. It'd be insane to do in real life because of performance, but it's really neat from a "hey look what you can do, isn't this cool!" point of view.
The first half of the book is pretty simplistic (at least for me, with some basic knowledge of Lisp and Scheme), but the second half really starts delving into some crazy things, passing lambdas around to lambdas and writing evaluators and stuff. But you almost don't even notice because of how solidly the foundations are built up to that point, and how well the examples are explained. (You probably will notice once you hit the y-combinator, on account of your brains detonating.)
This not a good book for "learning Lisp" in terms of learning the gritty details of to use a real-life implementation of Lisp. But it's a good book for learning some of the concepts that make Lisp and functional languages powerful. This is a very interesting and unique book, also highly recommended by many in the Lisp world. I'm already planning to try The Seasoned Schemer next.
Real World Haskell
Finally I started plowing through the recently-released Real World Haskell. This book is freely available online, which is great. It also allows readers to make comments on every paragraph in the book, which is a highly collaborative and probably very intelligent way to write a book, and also can provide insight for readers who don't understand the text at any given point. I may buy a hard copy, I haven't decided. (The online version has some FIXME notes and typos that I can only hope are fixed in the real thing.)
I'm only up to chapter 11, but it's good so far. It gives a ton of examples and goes slow enough for people completely new to Haskell to pick the language up. And it includes some of the mechanics of compiling and/or running programs in GHC, which is extremely helpful. I did start getting lost around the time monads were introduced, but that's to be expected. It usually takes me two or three good reads of this kind of book before I grok it all, which is no fault of the book.
Haskell. Last time I used it, I wrote Pong in Haskell in one of my college classes. It was an immensely painful experience. I'm going back to re-learn Haskell now because I find myself edging more and more toward functional languages and away from the C/Java world.
Haskell is far too much to swallow if you have no experience with functional programming, which is probably why I hated it in college. (That said, a smarter person than I may have fewer problems.) After graduating school, I got my first whiff of this world again via Perl's map and grep and friends. Then Ruby pushed it home for me with its widespread use of block parameters (lambdas in disguise). And after learning Lisp (and especially later, Clojure) I can't live without higher-order functions. If I had to write a classic for-loop and manually keep track of a counter variable I'd probably vomit at this point. Haskell takes function manipulation to an even greater extreme, which I still haven't fully wrapped my head around, but I like what I've learned so far.
Laziness is awesome, function currying is awesome, and this is the first time I've read about folding, apart from brief struggles with Ruby's (poorly named) Enumerable#inject. Pattern matching strikes me as vaguely similar to Common Lisp's / Clojure's destructuring-binding, which is one of the nicest features I've seen in any language when it comes to making a programmer's life easier.
Maybe it's all just interesting because it's still new to me. But I like the whole idea of functional programming. I like the idea of functions that always produce the same output from a given input. How and when to handle side-effects and object mutation is a problem that's always nagged at the back of my mind even when writing Ruby code.
That said, Haskell still (at this point in the book) does not strike me as a practical or real-world language in the slightest. You've got to jump through some crazy hoops to get a lot of things to work, especially when it comes to I/O. The book describes how to write a pretty-printer, and how to parse a binary file, both of which require some acrobatics to write concisely in Haskell. In particular the parser library lost me entirely; functions were being chained to functions in all directions and I couldn't follow it.
I think one reason Java is so popular is that you don't have to be a genius to write it. To write Haskell (or even Lisp) well, to take advantage of the language and use it smoothly, you really do need to do some deeper thinking. The abstractions are more powerful and more "abstract". It's not too hard to understand a world made of objects with a bunch of knobs you turn via method calls. Lambdas and recursive functions and monads and typeclasses are a more ephemeral thing.
That said I plan to stick with the book. Practice makes perfect.
Functional programming hurts me
This post will probably come back to haunt me, but functional programming doesn't sit well with me. I'm trying again to read through SICP. Last time I tried, I lost track of it and gave up after a few chapters. This time I hope to persevere, and I'm trying to go more slowly through the examples to really understand things.
I was trying to do some of the exercises in chapter 1 today, and pretty much bombing on all of them. Most of the exercises I've seen so far in SICP sound like they came out of a math book. "There is a clever algorithm for computing the Fibonacci numbers in a logarithmic number of steps..." Uh huh. This is not the kind of problem I enjoy, or have even the slightest use for. But maybe the problems are only pointless examples to illustrate a point, like "mass-less friction-less pulleys" in physics textbooks. I can live with that I guess.
Here is a Scheme example from SICP to compute b to the power of n:
(define (expt b n)
(expt-iter b n 1))
(define (expt-iter b counter product)
(if (= counter 0)
product
(expt-iter b
(- counter 1)
(* b product))))
This is presented as a wonderful and novel functional programming approach.
One of my problems is the amount of brain-work it takes to understand this. OK, start with EXPT. We take b and n, and then call some recursive helper function with b, n, and a state variable product initialized to 1 HEY WAIT A SECOND THERE'S A STATE VARIABLE?!
This is one way to cheat in functional programming, I guess. product is a variable name / symbol, which describes "something" that takes on different values over time, each value representing the same "thing" in different states. That qualifies as a state variable to me. The only difference is that the state variable is actually N different variables (with the same name) splattered across a bunch of frames on a call stack, instead of an abstraction of a single simple bucket where I dump a value.
Now in EXPT-ITER, counter starts at n, and every time the function calls itself, counter decreases by 1, until you reach 0, at which point EXPT-ITER stops calling itself and returns something. So this is a perverted, inside-out way of saying "do something N times", where every iteration costs me a recursive function call. Nice.
OK, what are we doing N times? We're multiplying product, which starts at 1, by b, which then becomes the new value of product next time around.
SO WHY NOT JUST WRITE THAT TO BEGIN WITH?
(defun simple-expt (b n)
(let ((total 1))
(dotimes (i n)
(setf total (* total b)))
total))
In Common Lisp this reads as "Let total start at 1. N times, set total to the product of total and b. Then return total." This is EXACTLY what the Scheme version is doing. Except I don't have to turn my brain inside-out to write it down this way. The CL code reads exactly like what the program is doing.
When all you have is a hammer, every problem looks like a nail. When all you have is recursion, every problem looks like it should be solved using recursion, I guess.
Actually if I ignore the functional programming bits, SICP is a great book. There are a lot of good things in there. But (so far) Scheme isn't one of them.
Bang!
In Scheme, an exclamation point (aka "bang") on the end of a procedure name indicates a procedure with side-effects e.g. a destructive procedure or mutator. This is only a naming convention for procedures and isn't understood or enforced by the language.
In Ruby you can also have an exclamation point on the end of a method name, and again it's only convention for programmers and means nothing for the language, but it's far less clear what a bang-method means in Ruby even by convention. A Google search reveals that lots and lots of people think it means something similar to Scheme: That a ! method changes the value of the calling object. On the other hand, David A. Black wrote a good article about his interpretation of !-methods in Ruby as nothing more than "the more dangerous of a pair of methods with the same name" for some value of "dangerous".
Well, not all methods that change the value or state of the calling object end with a bang. Array#pop and Array#push are obvious examples, but there are plenty of others. What about File.close? Thread.run? All of the []= methods? It makes sense that not all destructive methods in Ruby end in !. In Scheme or some other language where side-effect-free functional style is encouraged and expected, you need to flag methods that edit objects. If I "push" something onto a list in Scheme I probably won't expect my original list to be modified or destroyed in the process, unless the procedure was called PUSH!.
So given that not all mutator methods end in ! in Ruby, do all methods that end in ! change the caller object in-place? Almost. There are only 68 standard methods that end in !: About half of them are String and Array methods and the other half are a mix of things. Mostly all of those methods do some kind of in-place edit of the caller object. Interesting though is Bignum#power! which does NOT edit a Bignum in-place. Also interesting is Thread.terminate! which does edit the state of a thread (by killing it); but Thread.terminate ALSO kills a thread; the only difference between the two is how the thread is killed (whether it runs ensure blocks or not).
Outside the standard methods, there are things like OptionParser.parse! which destructively edits not itself, but another object (ARGV). And this seems to make plenty of sense to me, in spite of not being entirely consistent with other use of !.
Well then, is a !-method always simply the more dangerous of two methods with the same name? Usually. But oops, there is no Bignum#power to go with Bignum#power!.
So this ends up being somewhat confusing. What does ! in method names in Ruby actually mean? There doesn't seem to be a consistent answer, other than "Hey, this method does something that may be unexpected in some way".
Given that it's inconsistent, is it the case that there is a formal, strict, true definition of what ! means but that a handful of methods violate the definition? You could say that, though I'm not sure where that definition would come from. Maybe from the decree of Matz? Or maybe from the meaning of the convention in Scheme? Clearly Ruby ripped this convention off of Scheme.
But a convention is by definition simply a common usage of something. Convention allows for exceptions; if it didn't, then it wouldn't be a convention, it would be part of the syntax of the language. So maybe ! in Ruby means whatever the majority of people decide it means. Or maybe not.
