Tuesday, July 8, 2014

Being safer in dynamic languages

Jakub Arnold writes about Phantom Types in Haskell and compares the safety provided to a solution using Ruby. This isn't an "us vs. them" blog post. I'm not going to argue that dynamic languages are as "safe" as modern typed languages. But neither will I capitulate that only a modern typed language will suffice. Your mileage may vary. If we meet for beers, I'm sure we could have an interesting discussion. At minimum, we'd have a good beer and talk about something else.

Experienced programmers in either approach will be more successful than inexperienced programmers in any approach. Here is one way to be "safer" in a dynamic language, using the same example as the original post.

The original post lists Ruby like so:

    def send_message(message, recipient)
      if message.encrypted
        # send logic
        raise ArgumentError, "Can’t send a plain text message"

But a "safer" solution would look more object-oriented (I'll use Smalltalk):

    Tube>>send: aMessage to: aRecipient
        "Send aMessage to aRecipient where aMessage can represent itself as an encrypted string."
        | encryptedMessage |
        encryptedMessage := aMessage encrypted.
        "... send logic ..."

...where objects have the ability to encrypt themselves...

        "By default an object answers its string representation, encrypted."
        self asString encrypted

        "Answer the string encrypted using some agreed-upon algorithm."
        self tinyEncrypt

        "Encrypted strings just answer themselves."

Oh the monkey patching! First of all, Smalltalk has so much better tools than Ruby. Smalltalkers don't have to denigrate the practice as "monkey patching". It's just "programming with objects". Not a utility class in sight and no confusion about where any of the code originates.

As an aside, you may not want to encrypt large objects as strings in memory. (Or self-referencing objects, without a seralization that can handle them.) This is making a point about programming in general, not serialization or encryption.

But mostly you don't want to sprinkle "if"'s and unnecessary error handling throughout your system. If you want an encrypted object, just ask the object to provide its own encrypted representation. Implement the default case, the special cases, and the case where the object is already encrypted.

You might decide certain objects are just not suitable to being encrypted. For example the tubes themselves, or windows, or processes, etc. Those objects might signal an exception. Standard Smalltalk defines a signalling mechanism similar to Common Lisp's condition system, where certain exceptions might be resumable, etc. This is another safety mechanism of good dynamic languages. Maybe the topic of a future blog post. Or you can read Practical Common Lisp's really good description now.