Wednesday, November 30, 2005
Shape Code After Language II
If you shape your code after language then you can read your code aloud, and what is easy to say, it is easy to code.
In the previous post I mentioned how code shaped after language could be hard for other developers to read, and how easy it was for users. It looked like there was almost an inverse relationship between developer skill and code understanding :-)
Seriously, I think that as developers we have got used to a contrived way of writing code. Sometimes we get confortable in our own ways, with the language crutches and patterns that we have built. We are so used to our way of doing things that we cannot even conceive there can be a better way. I have been watching the whole java vs ruby diatribe on The Server Side and it amazes me how much java programmers were defensive in their ways. When they looked at ruby they couldn't see simplicity, just a threatening difference. I must say that those strong reactions played not a little role in my switching over to ruby.
While reading the bizarre Why's Poignant Guide to Ruby, I stumbled on this passage from chapter 3:
When you decide to start focusing on the human-oriented semantics of the language, a strange thinks happen - you will see that you will start focusing more on what things you want rather than on how you want to do them. Initially you are likely to experience it as frustration. You will feel like you cannot say what you want in simple human terms. You will want to go back to code to get stuff done, rather than trying to find the right words to compose some prose.
If you manage to overcome this feeling, maybe just setting some time aside to try to do it as well as you can, you will see that the way you use human words is changing the shape of your code. If you want to be understood by humans you will tend to talk in terms of intents, of things that you want, without going much in detail about how to do them. These details can be sketched out later on if necessary by exploding chosen parts of the code.
Let me show you a simple example example: choose those houses that cost less than 200'000 euros from a text file
An instinctive approach could be something like:
Iteration I
choose_those_houses_that_cost_less_than_200000_euros_from_a_text_file
we say what we mean. we start from the intent and we express it via human language
Iteration II
choose_houses_that_cost_less_than_200000_from(a_text_file)
we removed the "those", which didn't add much to the intent, and we say that the text file is a variation point. we have also implicitly expressed the intent of being able to change file. Note that we haven't expressed the intent of changing the selection criteria.
Also, I'm playing at home here. I am not going to specify that I am dealing in euros.
Iteration III
choose_houses_that_cost_less_than 200000, from=a_text_file
I dropped the brackets on arguments since I have decided that I am going to do this in Ruby. I have also created another variation point for the selection threshold. The second parameter is prefixed by a 'from='. What am I doing there? Suppress your coding habits and just read it aloud skipping the punctuation: choose houses that cost less than 200000 from a text file
It can't get much cleare than this, can it? This is what I call prose - code transliteration without punctuation and symbols.
By the way, the 'from=' is a trick that consists in the implicit creation of the local variable 'from' before passing its value to the function. You could have also used a hashmap in Ruby. Something like: :from => a_text_file. That would have been more in the Ruby Way kind of style, but for today I want it my way.
Iteration IV
choose_houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
reading it aloud: choose houses that cost less than 200000 from the text file "houses.txt"
I have added the possibility of specifying the file name. I have also accomodated the longer file specification, changing the sentence from
to
'from' has become 'from_the' for no other reason than it sounds better this way when close to its neighbourgs.
This intent could have also been specified as:
choose_houses_that_cost_less_than 200000, from_the_textfile="houses.txt"
but in this case I just took the opportunity to wrap up a bit of meaning (the concept of textfile) and keep it outside the function that I am calling. What is the return value of textfile("houses.txt") ? It could be the text of the file. It could be a File object. It could be a parsified collection of houses. Or maybe it's just a string.. maybe it's just returning the argument itself! Who cares? The overall meaning is clear. We are not refactoring code, we are doing intent refactoring here.
If you are relying on your hard won coding instincts this will drive you crazy. Just deep breathly and read the code aloud.
Iteration V
chosen_houses = houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
puts chosen_houses
I have added the code to keep my selection into a variable and the code to print out this selection to screen.
Initially I wrote:
chosen_houses = choose_houses_that_cost_less_than 200000, from_the=textfile("houses.txt ")
but I din't like it. It must be due to the repetition of 'choose'. chosen equals to what we choose. It's not bad, but it's not great either. The language is still young and we can mould it to what appeases our ear. Generally speaking, when I find two similar words in a row (chosen and choose) I try to keep one and drop the other. In this case "chosen_houses" becomes simply "houses".
The call to the function "houses_that_cost_less_than" simply return houses. It returns what it states: houses.
When I print it out I use the ruby method "puts". I don't like it a lot. I would have rather used:
print chosen_houses
or
chosen_houses.display
but 'puts' is truly ubiquitous in Ruby and this time I felt that complying with the confortable habit of just typing those four easy letters was the more expressive thing to do.
Iteration VI
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
puts chosen_houses
Now there is some meat! I have defined what I mean by that function, and I have said it in Ruby's own terms. We can't go much lower level than that. Fortunately Ruby is very legible, and this code is expressive even in its raw form. If I read it aloud it is: [from] houses select [an] house [when] house.price < a threshold
In a different language it would have been much less legible. When you read a chunk of code, it should convey its meaning - its intent - in seconds. If it doesn't it needs intent refactoring. A readable, easily digestible chunk of code, is called Stanza, just like the stanzas of a poem.
Note that by writing that code in such a way that it is expressing its intent in the simplest way, we have also implicitly taken a design decision. 'houses' is an enumerable object containing house elements that have a price.
Iteration VII
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
def houses_file(filepath)
File(filepath).readlines.collect { |line| house_from line }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=houses_file("houses.txt")
puts chosen_houses
I have decided to make textfile("houses.txt") into houses_file("houses.txt") . I was pushed to do this by the decision of returning a set of houses. I wanted to make it slightly clearer without compromising the expressiveness of the code. This is another important trade-off. It's not like language-level syntax has no importance, it's just that it should come second after expressiveness. In this case this change also akes the intent clearer, by specifying that the file is a special kind of file, a house file.
We have also defined the insides of this function. Once again it was so simple to express it in ruby that it was pointless to detail it further: [from the] File [in] filepath read lines [and] collect [every] line [by getting an] house from [the] line
This is a bit more complex, but if you do Ruby it's clear at a glance. Note that I say File(filepath) and not File.new(filepath).
I don't like constructors. Constructors are for objects, not for people. And we don't want to see objects, we want to see intents. We want a File made from a filepath, we are not interested in creating objects. I don't need a "new", I just need to say that I want a File. When I read I don't want to see that File is an object that gets constructed there and the "new" just obscures the meaning. In Ruby it's pretty easy to define an implicit constructor so that the 'File(filepath)' syntax works without any problem
We have also defined a 'house_from' that is going to parse a line and produce a 'house' object.
Iteration VIII
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
def house_from(text)
city, address, price = text.split(",")
return House(city,address,price)
end
def houses_file(filepath)
File(filepath).readlines.collect { |line| house_from line }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=houses_file("houses.txt")
puts chosen_houses
We have defined house_from where we split the text along commas and get the information that we use to define a 'House'. We could have done it all in a single operation:
return House *text.split(",")
but we would have lost the meaning of the fields extracted from the text line. House is another implicit constructor.
Iteration IX
class House
attr_accessor :city, :address, :price
def initialize(city, address, price)
@city=city; @address=address; @price=price
end
end
in this iteration we conclude by definining the House in simple terms.
In the previous post I mentioned how code shaped after language could be hard for other developers to read, and how easy it was for users. It looked like there was almost an inverse relationship between developer skill and code understanding :-)
Seriously, I think that as developers we have got used to a contrived way of writing code. Sometimes we get confortable in our own ways, with the language crutches and patterns that we have built. We are so used to our way of doing things that we cannot even conceive there can be a better way. I have been watching the whole java vs ruby diatribe on The Server Side and it amazes me how much java programmers were defensive in their ways. When they looked at ruby they couldn't see simplicity, just a threatening difference. I must say that those strong reactions played not a little role in my switching over to ruby.
While reading the bizarre Why's Poignant Guide to Ruby, I stumbled on this passage from chapter 3:
Read the following aloud to yourself.
Yep, do just as Why says: read the code aloud.
When you decide to start focusing on the human-oriented semantics of the language, a strange thinks happen - you will see that you will start focusing more on what things you want rather than on how you want to do them. Initially you are likely to experience it as frustration. You will feel like you cannot say what you want in simple human terms. You will want to go back to code to get stuff done, rather than trying to find the right words to compose some prose.
If you manage to overcome this feeling, maybe just setting some time aside to try to do it as well as you can, you will see that the way you use human words is changing the shape of your code. If you want to be understood by humans you will tend to talk in terms of intents, of things that you want, without going much in detail about how to do them. These details can be sketched out later on if necessary by exploding chosen parts of the code.
Let me show you a simple example example: choose those houses that cost less than 200'000 euros from a text file
An instinctive approach could be something like:
- open the file
- parse it line by line
- put houses data in a container
- loop on the container
- check for condition < 200'000
- if the condition is satisfied print out the names
Iteration I
choose_those_houses_that_cost_less_than_200000_euros_from_a_text_file
we say what we mean. we start from the intent and we express it via human language
Iteration II
choose_houses_that_cost_less_than_200000_from(a_text_file)
we removed the "those", which didn't add much to the intent, and we say that the text file is a variation point. we have also implicitly expressed the intent of being able to change file. Note that we haven't expressed the intent of changing the selection criteria.
Also, I'm playing at home here. I am not going to specify that I am dealing in euros.
Iteration III
choose_houses_that_cost_less_than 200000, from=a_text_file
I dropped the brackets on arguments since I have decided that I am going to do this in Ruby. I have also created another variation point for the selection threshold. The second parameter is prefixed by a 'from='. What am I doing there? Suppress your coding habits and just read it aloud skipping the punctuation: choose houses that cost less than 200000 from a text file
It can't get much cleare than this, can it? This is what I call prose - code transliteration without punctuation and symbols.
By the way, the 'from=' is a trick that consists in the implicit creation of the local variable 'from' before passing its value to the function. You could have also used a hashmap in Ruby. Something like: :from => a_text_file. That would have been more in the Ruby Way kind of style, but for today I want it my way.
Iteration IV
choose_houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
reading it aloud: choose houses that cost less than 200000 from the text file "houses.txt"
I have added the possibility of specifying the file name. I have also accomodated the longer file specification, changing the sentence from
from=a_text_file
to
from_the=textfile("houses.txt")
'from' has become 'from_the' for no other reason than it sounds better this way when close to its neighbourgs.
This intent could have also been specified as:
choose_houses_that_cost_less_than 200000, from_the_textfile="houses.txt"
but in this case I just took the opportunity to wrap up a bit of meaning (the concept of textfile) and keep it outside the function that I am calling. What is the return value of textfile("houses.txt") ? It could be the text of the file. It could be a File object. It could be a parsified collection of houses. Or maybe it's just a string.. maybe it's just returning the argument itself! Who cares? The overall meaning is clear. We are not refactoring code, we are doing intent refactoring here.
If you are relying on your hard won coding instincts this will drive you crazy. Just deep breathly and read the code aloud.
Iteration V
chosen_houses = houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
puts chosen_houses
I have added the code to keep my selection into a variable and the code to print out this selection to screen.
Initially I wrote:
chosen_houses = choose_houses_that_cost_less_than 200000, from_the=textfile("houses.txt ")
but I din't like it. It must be due to the repetition of 'choose'. chosen equals to what we choose. It's not bad, but it's not great either. The language is still young and we can mould it to what appeases our ear. Generally speaking, when I find two similar words in a row (chosen and choose) I try to keep one and drop the other. In this case "chosen_houses" becomes simply "houses".
The call to the function "houses_that_cost_less_than" simply return houses. It returns what it states: houses.
When I print it out I use the ruby method "puts". I don't like it a lot. I would have rather used:
print chosen_houses
or
chosen_houses.display
but 'puts' is truly ubiquitous in Ruby and this time I felt that complying with the confortable habit of just typing those four easy letters was the more expressive thing to do.
Iteration VI
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=textfile("houses.txt")
puts chosen_houses
Now there is some meat! I have defined what I mean by that function, and I have said it in Ruby's own terms. We can't go much lower level than that. Fortunately Ruby is very legible, and this code is expressive even in its raw form. If I read it aloud it is: [from] houses select [an] house [when] house.price < a threshold
In a different language it would have been much less legible. When you read a chunk of code, it should convey its meaning - its intent - in seconds. If it doesn't it needs intent refactoring. A readable, easily digestible chunk of code, is called Stanza, just like the stanzas of a poem.
Note that by writing that code in such a way that it is expressing its intent in the simplest way, we have also implicitly taken a design decision. 'houses' is an enumerable object containing house elements that have a price.
Iteration VII
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
def houses_file(filepath)
File(filepath).readlines.collect { |line| house_from line }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=houses_file("houses.txt")
puts chosen_houses
I have decided to make textfile("houses.txt") into houses_file("houses.txt") . I was pushed to do this by the decision of returning a set of houses. I wanted to make it slightly clearer without compromising the expressiveness of the code. This is another important trade-off. It's not like language-level syntax has no importance, it's just that it should come second after expressiveness. In this case this change also akes the intent clearer, by specifying that the file is a special kind of file, a house file.
We have also defined the insides of this function. Once again it was so simple to express it in ruby that it was pointless to detail it further: [from the] File [in] filepath read lines [and] collect [every] line [by getting an] house from [the] line
This is a bit more complex, but if you do Ruby it's clear at a glance. Note that I say File(filepath) and not File.new(filepath).
I don't like constructors. Constructors are for objects, not for people. And we don't want to see objects, we want to see intents. We want a File made from a filepath, we are not interested in creating objects. I don't need a "new", I just need to say that I want a File. When I read I don't want to see that File is an object that gets constructed there and the "new" just obscures the meaning. In Ruby it's pretty easy to define an implicit constructor so that the 'File(filepath)' syntax works without any problem
We have also defined a 'house_from' that is going to parse a line and produce a 'house' object.
Iteration VIII
def houses_that_cost_less_than( a_threshold, houses)
houses.select { |house| house.price < a_threshold }
end
def house_from(text)
city, address, price = text.split(",")
return House(city,address,price)
end
def houses_file(filepath)
File(filepath).readlines.collect { |line| house_from line }
end
chosen_houses = houses_that_cost_less_than 200000, from_the=houses_file("houses.txt")
puts chosen_houses
We have defined house_from where we split the text along commas and get the information that we use to define a 'House'. We could have done it all in a single operation:
return House *text.split(",")
but we would have lost the meaning of the fields extracted from the text line. House is another implicit constructor.
Iteration IX
class House
attr_accessor :city, :address, :price
def initialize(city, address, price)
@city=city; @address=address; @price=price
end
end
in this iteration we conclude by definining the House in simple terms.