This is a small template that teaches you how to write a function. This template distills a number of great practices into a brief checklist. This short checklist, which is the core teaching of How To Design Programs book, concentrates writing code, documentation, tests, and thinking about your function's input range in one small checklist.
The book uses Racket as a teaching language. I am going to give an example using Ruby.
The checklist
Now let's walk through a business software example. Let's say you want to insert a new blog record from an http request.
We write the function with its name. Nothing more. Notice I am not writing the body or the parameters. Only write the name so that it is syntactically correct in the language that you are working on.
def create_post()
end
This comment documents what our intent for the function is. When designing a function, it helps us focus on what we are trying to write. If you can't express it in words, you can't express it in code.
# creates new post
def create_post()
end
Even if you work with a dynamic language you are working with types. The point of this step is to explicitly think about what is your input and what will be your output. Statically typed languages will enforce your type signature for you. If you are using a dynamic language, you need this information to know what is supposed to be happening.
The book uses the following type format. You make a list of each parameter followed by an arrow (->), ending with the return type. This will be familiar to people who have studied Haskell or OCaml. If the return is void, you can say "void" or "unit".
# creates new post
# Hash -> Unit
def create_post()
end
This is an example using more idiomatic Ruby.
# creates new post
# @param params, Hash, the http parameters hash
# @return void
def create_post(params)
end
Writing an example of how to use your function helps you further design it. It is also one of the most helpful documentation you can add. Most people will often google for examples. Include them.
# creates new post
# @param blog_parameters, Hash, the http parameters hash
# @return void
# Example: create_post(params[:blog])
def create_post(blog_params)
end
Notice how I changed the name of the parameter. I did this so that the example would be less confusing since I wanted to pass 'params[:blog]'. Thanks to writing the example, now I have a more explicit name for my function signature.
This is a two part step. We are asked to write tests for the correct output and for input ranges. So the happy path and the boundary checks.
Let's start with basic correctness when everything is right, the happy path. # creates new post # @param blog_parameters, Hash, the http parameters hash # @return void # Example: create_post(params[:blog]) def create_post(blog_parameters) end
# testing correct behavior or happy path
# The test is ruby pseudo code
def test_creation()
# setup
params = {title: "hello", body: "hello world!", author: "Joe Smith" }
# exercise
create_post(params)
# verification
record = ORM.find_by(title: "hello")
assert !record.nil?
end
This made us discover that we we have three required parameters. Now we can change our function again with this new information.
# creates new post
# @param title, String, title of the blog post
# @param body, String, body of the blog post
# @param author, String, body of the blog post
# @return void
# Example: create_post(title, body, author)
def create_post(title, body, author)
end
Now we can think about input violation and ranges. How short can a title be? And how short can a body be? What should happen we send a null value for author? What if the inputs are too big? Is it okay to have no author?
Thinking in term of ranges helps us identify the right number of tests. This is a superior way thinking about it than old advice I was told for TDD where you kept coming up with tests until you couldn't think of any more.
Now we are ready to write the body of the function. The process has made it clear what we need to do. Now we can execute.
# creates new post
# @param title, String, title of the blog post
# @param body, String, body of the blog post
# @param author, String, body of the blog post
# @return void
# Example: create_post(title, body, author)
def create_post(title, body, author)
blog = Blog.new(title, body, author)
ORM.save(blog)
end
In the book they have created a whole library of templates for the find of work it is needed to do. Some functions require looping, so they have template looping code. If you have time, explore that library. It most likely will speed up coding if you think in those terms.