Wednesday, November 30, 2005
Shape Code After Language II
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.
Saturday, November 26, 2005
Shape Code After Language
The idea of deriving design concepts from user vocabulary is not a new one.
I remember that modelling felt like planning a platonic ideal world, later to be incarnated in code. The clouds would have opened showing the shining design behind them, as a beam of pure rational light would have moulded the humble software clay into an earthly embodiment of my model. The code was a mere detail.
Needless to say, I drank too much coffee, I started wearing black turtlenecks and I didn't get much done at all.
I developed a bad case of writer's block. On one hand I couldn't go back to 'just coding'. On the other hand the design hyperuranium was so full of possibilities that I just didn't know where to start from without tying myself up in conceptual knots.
Anyway, while I was floating in Plato's hyperuranium, somehow it occured to me to read the missing chapter. It is there that I found a simple powerful technique.
1. Write down what you understand about the domain in plain English.
2. Nouns should be classes
3. Verbs should be methods
Design was just the intellectual hubris of neo-platonic structured thinking and intelligent design theories. The stuff that worked evolved from the bottom. The intelligence was not in the coder and was not in the code, but somehow it was in the whole, it was emerging and unfolding as all the parts interacted, respectfully listening to each other and adapting around the problems.
The users had the model. We were just trying to dig it out through careful questioning.
These days I try to evolve my code by the motto: "If it's easy to say, it should be easy to code".
Now, the thing is that 99% of user requests are very simple and reasonable requests within the users' own model. If you can distill that model, you will be able to make them feel understood. And it's true: you will genuinely understand them!
Your software will have become a true nooartefact - an artefact encoding business knowledge in executable form. Much more than what we think of as being an application.
Evans had some very profound insights, yet I see people that have read the book asking questions like "should a Repository be implemented as a Singleton?", as if DDD is dealing with fixed patterns. I believe that Evans has opened a door on fantastic new possibilities, but most of the community is still fixated on the door frame and doesn't see that there is something beyond the threshold.
The philosophy of DDD inexorably trickled down to the VBA code. My code started taking new shapes, struggling against the rigidity of the programming language. Then I started applying a few tricks that Evans suggested in the book. Things such as method chaining and object algebras.
Then the unthinkable happened. I gave in to Vb.
I stopped trying to write java code in Vb and I just tried to do the simplest thing within the Vb language.
I had module constants without prefixes indicating which module they were from. I was accepting untyped parameters everywhere. I used singletons. I made heavy unnecessary use of named parameters. I abolished the use of 'New' and I had functions to build even the simplest objects. Except that sometimes the functions were building objects and sometimes they were not, and you had no way of knowing without looking at the code.
That was when I discovered Shal - Shape (code) After Language
Wednesday, November 23, 2005
Ruby Facets
stay tuned.
Monday, November 21, 2005
Do With
very nice and readable.one = ['a', 'b', 'c']
other = ['x', 'y', 'z']
one.do_with(other) { | a, b | puts a + b }
I think I am going to add Dave to my Links menu.
Sunday, November 20, 2005
Instant Intent
These poor man's "testing" functions allow you to express the intent of a piece of code and later run a reality check against it. However, these days I don't like using the 'test' word, but I prefer to think in terms of intents and reality checking.
Dave Astel calls them behaviours, but I think I will stick to intent for the time being, just to avoid polluting the semantics of the concept I want to explore.
Some things you can do with intents:
expect 5, (3+2)
(3+2).should_be 5
expect_about Pi, 3.14159
expect_exception (StandardError){
puts 5/0}
Code as Language II
Let's see what I can get from a first quick pass..
Excerpt VII
str = "Hello, \n\n world!"
str.define_method(:heading) {
('*** ' + self.join + ' ***').indent(3)
}
str.heading # -> " *** Hello, world! ***"
very nice for metaprogramming. metaprogramming in ruby can quickly get quite messy if you are not careful to continuously refactor your intent.
Excerpt VIII
person = OpenStruct.new do |p|Ah, I have written stuff like this in the past! This is very much in the spirit of the nooclay. Maybe I should publish my Clay code one of these days.
p.name = 'John'
p.age = 90
end
Excerpt IX
class Personautoinit is v. useful. that's another bit of nooclay behaviour.
attr_reader :name, :age, :pension
autoinit :name, :age do |p|
@pension = 30000 + (@age - 65) * 350
end
end
Excerpt X
HELP = %{Ah, this is similar to Brian Marick's code trick. I like it. Text should become a first class citizen of code.
| Usage: ...
| Lorem ipsum...
}.trim('|')
Excerpt XI
STR = "Hello, world!"
STR.starts_with? "Hello" # -> true
STR.ends_with? "world" # -> false
File.write("hello.txt", STR)
Of course. of course!
Excerpt XII
arr = [1, 3, 5]I wrote it myself in the past. very useful and practical.
arr.rand # -> 1, 3, or 5
arr.none? { |n| n.even? } # -> true
arr.one? { |n| n > 4 } # -> true
Excerpt XIII
Class.by_name "Process::Sys" # -> Process::Sysagain, very useful metaprogramming.
Process::Sys.basename # -> "Sys"
Excerpt XIV
Binding.of_callerThis allows you to access the context of the caller of a function.
How do they do it? I have to find out.
Very useful and very dangerous.
I can see lots of interesting work been done here by exploiting the context of a call.
Nooclay
Imagine a software material so advanced and yet so simple to interface with. Not just a few intents, but the evolving semantic universe of human knowledge.
Imagine a semantically rich material that changes shape in your hands, following your hints, your nudges, and at the same time offering the forms of the culture it has been tuned to.
Imagine Nooclay.
A sort of nooclay is already possible. A software material with a cultural bias that is opinionated and makes assumptions, yet allows you to bend it to new shapes.. even if it give its best, it is really smooth when following that specific bias, the stylistic school it has been tuned to.
Code as Language
After reading some of Brian Marick's stuff I have been wondering if there were other projects on the web working to the same goal.
Guess what? If you can think it, on the internet you find it :-)
Here is a small review and lots of cut&paste, mainly from comp.lang.ruby..
From "Readability" inflation:
Excerpt I
Similarly, if you're used to:
f = lambda {|a,b| ... }
then after a while, you might find that so natural that you feel
"ready" for
Well, of course, it allows you to write more uniform code, transforming the block definiton into a tool to define new syntax.
Excerpt II
> def foo(a =>, b =>, c =>)
> obj = -> (a, b = 1){ block }
def foo(a:, b:, c:)
Yep, it does. Much better.
I used to think of named parameters (an Ada idiom, if I am not wrong) as a bureaucratic heritage from dying programming languages. These days I think they are fundamental to write readable programs. How else would you know the meaning of a parameter, without chasing words with the mouse to get tooltips to pop up?
Still, I think they should be optional. There is no point in saying:
plot_point x: x, y: y, color: chosen_color
since this would make perfect sense:
plot_point x, y, chosen_color
Excerpt III
Consider Microsoft's Monad's
get-childitem | sort-object extension | foreach { $_.filename}
vs Ruby's
get-childitem.sort_by{|child| child.extension}.map{|child| child.filename}
without adding any readability that I can see over the Microsoft version.
Must agree with Microsoft, just this time. The implicit result variable makes beautiful code possible.
Excerpt IV
> have one.
String contains "abc"
0..10 contains 5
1,2,3,4 contains 3
/^[a-z]*$/ includes "apple"
It would be nice if Array#=== was aliased to Array#include? So that
somearray === someval
Very nice, I'll check if it has been aliased in any ruby library
From Ruby Facets 0.6.2:
Excerpt V
Perhaps it would also be a nice idea to add Enumerable#count,
Kernel#with and Object.new with a template block?
Samples:
[1, 2, 3].count { |item| item > 1 } # => 2
with("foo") { reverse } # "oof"
obj # => "foo"
obj.reverse # => "bar"
This is the way I understood how 'with' would work:
s = "hello"
with(s) {
reverse!
capitalize!
I like the count, it reminds me of the COUNTIF in Excel. Actually I would rename it to count_if.
The 'with' is quite cool. How do they do it? I wonder how it is implemented.
I like to be able to make context explicit.
Excerpt VI
Thanks for that. Can I humbly suggest that Numeric#ago be changed to
Numeric#before? If we're trying to mimic English, you'd never say "10
days ago yesterday" in English, but you would say "10 days before
yesterday". So instead of making people call
10.days.ago( Time.now - 1.day )
they could call
10.days.before( Time.now - 1.day )
instead. And if Time.yesterday and Time.tomorrow had been defined, you
could simply call
10.days.before Time.yesterday
And maybe this would cause transitional issues, but it might be good to
redefine Numeric#ago to refer to a time relative to the current moment,
which is in line with what it means in English.
yesterday = 1.day.ago
last_monday = 8.days.ago
Similarly, Numeric#later could go the other way:
tomorrow = 1.day.later
next_tuesday = 6.days.later
And Numeric#after could add time to any arbitrary starting point:
very nice, I am impressed. great readibility.
I'll explore some more examples and possibly some libraries. I will also try to organize the material that I find in some categories.
Saturday, November 19, 2005
Evolving Maru Batsu from Intents
Wednesday, November 16, 2005
Liquid Developers
When I think about these people, I think of them as a new kind of reinassance avant-garde.
I think of them as being Liquid Deveopers.
Blogging Plans
After a long time of timid wall-papering I finally took the step of signing up for a blog, and I now want to start writing up some of those notes, hoping that the idea of a potential public will help me to focus thoughts and developments in a productive way.
Brian Marick is my hero
I don't know if you are reading this Brian, but I really dig your style. Kudos to you and please keep up with the excelent work.
Tuesday, November 15, 2005
Liquid Languages
Tuesday, November 08, 2005
Four Stages of Mastery 2
The Adept – makes it modular
The Master – makes it elegant
The Enlightened Master – breaks the symmetry
Four Stages of Mastery
The Adept – thinks about structure
The Master – thinks about intent
The Enlightened Master – thinks about people
Thursday, November 03, 2005
Liquid Materials
The Web is a medium. Wiki is a medium. As strange as it may sound, Excel is often a medium.
The whole concept of Mashup and Web2.0 is a new medium in itself, allowing to build things from it. A Mashup, as far as I understood it, is an "application" that uses existing services and information from the web, collating them into a new knot of active executable knowledge, not originally present in any of its parts.
YubNub is another example of emerging medium. YubNub concept is so simple and powerful that its implications take your breath away. YubNub is about building a command line to the Web. On the Web. This is a medium bootstrapping itself to new heights!
Envision the level of integration you could achieve when a tool like YubNub and web applications (and services) start moving in step, striving to achieve reciprocal leverage.. this is a whole new world of knowledge at your fingertips, embeddable into smart servants and enhanceable via human interaction.
The Web as a Medium
A program has a set of expectations on user interactions and well-formed steps that must be followed to obtain certain results. If the program has been designed to do a certain thing, then you have to struggle against the program if you would like it to do something different.
A medium has a more liquid nature.
A medium is based more on conventions than strict rules.
A medium suggests, but doesn't enforce.
A medium is made for people to move through it, leave signs, start jobs and leave them there to be completed later.. maybe for someone else to see them and add their own bit to them. A medium is somewhat loose and open-ended, allowing continuous semantic hijacking by its users.