Monday, 9 July 2012

On Types and Tests

There's been a couple of posts recently (Confession of a Haskell Hacker and Unit Testing isn't enough) about types and tests. I thought I'd write down some of my half-baked thoughts about how I think about types and tests.

When I'm writing code, I tend to try to write them in a functional-style. Given some arguments, produce some output. Give the same arguments, get the same answer. This isn't possible everywhere in most applications, but I've found it to be applicable most of the time. Given I've got this function, I now need to test it. I like to think of each function as having a shape.

Let's consider a simple function that scores a Poker hand. Our first signature for this is:

// And so on.
  const int HEARTS = 1;
  const int KING = 10;

  int ScoreHand(PairOfInts[] cards) {}

If I was to visualize this function, I'd imagine something like this:


The values of possible input to this function are HUGE.  It's defined over every single possible pair of integers.  The only interesting area is the tiny red dot in the middle (not to scale) that represents the tiny amount of the possible input that it's possible to return a valid value.  As is already clear this isn't a good way to write a function - you can pass almost anything in, and the chances of getting a sensible response is slim.  The testing burden on this function is huge, whereas the type "burden" on the function is very low.

Let's try and improve things a bit.

   enum Suit { Hearts, Diamonds, Clubs, Spades };
   int ScoreHand(PairOfSuitAndAnInt[] cards) { /* reams of code */}   

This is better, the size of the red circle has increased a tiny amount, but there's still a huge amount of code that it can't (sensibly) produce a value for. This is known as a partial function. We have to test slightly less (no need to test that a Suit is valid), but we still have to consider a huge range of other options (what if there are six cards?).

What we're after is aligning the function so that for every value we can calculate a suitable score.

   enum Suit { Hearts, Diamonds, Clubs, Spades };
   enum Value { One, Two, Three, Four, Five, Six
                 Seven, Eight, Nine, Ten,
                 Jack, Queen, King, Ace };

   class Card {
     public final Suit suit;
     public final Value value;
     // And an appropriate constructor
   }

   int ScoreHand(Card a, Card b, Card c, Card d, Card e) { /* reams of code */}


Now I'm closer to total function. It guarantees (via the type system) to produce a sensible value for each and every input that's passed to it. (I'm just going to willfully ignore that Java/C# allows null values. Let's pretend it doesn't!). The big hole we have is that we don't check the uniqueness of the cards (e.g. I could ask for the score of five aces!).

If I visualize the function now, it's much (that's an exaggeration) more interesting.  Red defines the area that the function produces a sensible value for and the area around the outside is the inputs we don't provide a sensible output for!



Now I can go about testing this in a much nicer way. Traditional unit testing is existential quantification. Given this set of values, I expect this kind of output. It gives some assurance for some values. I'll need some of these tests just as a sanity check.

Property-based testing extends this. In property-based testing I can say things like given any input of the form needed by the function, the following holds. For example, I could write a test that said given 5 unique cards, if I compare them (with the output of the ScoreHand function) I'll lose against a Royal Flush (unless I also have a royal flush).   If you have a total function, Property-based testing allows universal quantification - you can specify an invariant that holds for all values of input.

You should use your type system (if you have one) to constrain the inputs to your functions as much as possible. The more constrained the types, the less you have to test (you still need both!). If you can define a total function, and a set of invariants that hold for all values then you have a incredibly powerful test-suite.



Friday, 29 June 2012

Sensible Not Censorship

The Government has recently started a consultation on Parental Internet Controls. I strongly believe that this is completely the wrong way to address the issue.

The Internet doesn't present any more of a threat to children than the newsagent round the corner, or your older brothers collection of pornographic magazines. The idea that we can solve the problem of children accessing "unsuitable" content by technical means is laughable.

One of the ideas in the proposal is "active choice":

Active Choice: customers are presented with an unavoidable choice or series of choices through which they consciously choose whether or not they want filters and blocks installed on their internet service or internet-enabled device

This is a poisonous idea that must be stopped! I don't want every ISP in the country to have a record that "Jeff likes his internet unfiltered, but Joe doesn't". The idea of a Government appointed set of goggles on the Internet is not attractive in the slightest!

How's it going to work? Let's imagine that little Joe wants to see some pornography. His parents have opted for filtering, so he's completely safe right? His first search for "breasts" returns no titillating content whatsoever so he gives up. Of course he doesn't! He starts to search for ways around the shield. What's next, do we block all information on proxy servers or Tor, or any other of the million and one ways that any technological solution can be by-passed?

The solution to this problem isn't technological.

It doesn't cost any money.

It doesn't need much resourcing.

It's very simple.

Let parents be responsible for what their children see online.

It's sensible not censorship.

Please read the proposal and give feedback before its too late.

Sunday, 29 April 2012

Thoughts from the final day of ACCU

The final day keynote was a series of lightning keynotes (apparently due to a missing speaker during the Icelandic Volcano problem a few years before and it now being an established tradition).

The opening key note was by a Lisper, Didier Verna and was about language obesity.  The central argument was a familiar one from a Lisp expert, but I still found it pretty compelling!  Natural languages have evolved from a few roots, and have evolved by adding new words, but the grammatical structure has remained mostly static (when was the last time a new grammar structure was added to your language?).  In contrast, the rationale for most new language design tends to involve taking the best bits from previous languages, and adding a sprinkle of some new grammatical elements to simplify things.  For example, both C# and Java have evolved to have for-each loops with new syntax.  C++ is perhaps the best example of language bloat - C++ 11 is probably the most complicated language I have ever seen with very unclear semantics.

Lisp doesn't suffer from this problem (it suffers from syntactic anorexia!), and the reason it doesn't is homoiconicity.  This property is that code is data (and vice-versa).  For example, in Lisp (+ 1 1) is both an expression that applies 1 and 1 to the + function, it's also a list of three expressions atoms.  This property gives Lisp the ability to add new syntax and structures without the need for a new language.  CLOS is perhaps the best example of this, adding an object system to the base language without the need to change the language specification whatsoever.  The loop macro is another good (I'm not a fan; I don't want to learn a big DSL for dealing with collections) example of this.  Someone will probably argue that Boost Spirit is a great example of how you can use template metaprogramming to do the same thing in C++ but that's a Turing tarpit style argument!

Lisp will be around for ever in one form or another simply because of this property.  I'm not sure whether the same can be said of curly-brace languages.  What horrific new structures will be needed in a decade when we're programming on mega-core machines?  I found the argument compelling, and perhaps in light of the previous keynote on the Requiem to C there's a chance that a new homoiconic language will emerge to deal with our multi-core future. 

Charles Bailey talked about the "Important Art of Thinking".  Despite the fact that software should always be about thinking, we sometimes find ourselves developing auto-pilot and thus end up in a mess.  It's always easier to start tapping on the keyboard rather than thinking about the problem (working in Haskell inverts this for me!

He mentioned the Dreyfus model of skill acquisition and tried to relate this to programming by peeling back the layers of abstraction on std::endl.

     
  • Novice - std::endl ends a line with a newline
     
  • Advanced Beginner - std::endl is a manipulator
     
  • Competent - std::endl can be viewed as a function operating on the stream
     
  • Proficient - std::endl is defined as a function template
     
  • Expert - std::endl is redundant when connected to a terminal because it will (by POSIX standards) guarantee at least line-buffering.  Just use '\n' already!


I do like the new C++ lambda sequence.  Being able to type [](){}() and have it mean nothing is an achievement by anyone's standards!  In summary, think before coding...

Next up was Emily Bache talking about "Geek Feminism", more specifically programmer geek feminism.  This was defined as the ability for women to influence the programming community (on merit - no-one was asking for a free pass!).  As was evident at the conference, there aren't a lot of women in technology.  In the grand scheme of things, there aren't a lot of software engineers at all, so missing out on 50% of the population seems like a big problem!  Imposter syndrome was something I am familiar with (especially in my first programming gig having come out of a research background).  It's the feeling that you think you are crap despite external evidence that you are good.  There was anecdotal evidence that women are more prone to suffer from this than men.  There was also a reference to the Wisdom of Crowds which (rather obviously) states that gathering opinions from a bigger range of backgrounds leads to better results.  Software's gender inbalance stops us taking full advantage of the wisdom of crowds.

Finally, there was a great presentation from Jurgen Appelo on finding your motivation.  Jurgen presented CHAMPFROGS, an acronym for the various things that can motivate developers.

     
  • Curiousity
     
  • Honour
     
  • Acceptance
     
  • Mastery
     
  • Power
     
  • Freedom
     
  • Rrelatedness
     
  • Oorder
     
  • Goal
     
  • Status


The Moving Motivators game sounds like something useful to try to see if your needs and desires are aligned with the work you are actually doing.

Next I went to Usable API's in Practice by Giovanni Asproni.  This tried to fill the gap between the principles that we have (Single Responsibility, Principle of Least Astonishment, Don't Repeat Yourself) and the code that we write.  API's are the biggest asset (or liability) that a company has.  A bad API (even an internal one) limits your ability to change and can act as a productivity drain.  The following principles were presented.


     
  • Code from the users perspective - One example of how to do this is not just to test your API, but also to test a program written using your API.
     
  • Naming - keep it simple, don't be cute and use one word per concept
     
  • Control to the caller - don't restrict options.  Previously I've worked somewhere were all the core container functions used their own locks.  This was a terrible decision because the caller didn't have control!
     
  • Explicit Context - don't use globals to hide context from the user, pass it in at construction time
     
  • Error Reporting - one of the harder ones.  Use layers (since an error at one layer is not necessarily an error at the next).
     
  • Logging - this gave some good discussion on why Logger logger = LoggerFactory.GetLogger() is a bad thing!


I chose to leave it there at ACCU.  I really enjoyed most of the talks.  The key themes this year were multi-core is coming, TDD is good and functional is fun.  I suspect these have been messages from the expert community for quite some time, but hopefully the wider software engineering community will start to realize this!