[clue-talk] Stupid OCaml tricks

Matt Gushee matt at gushee.net
Mon Aug 14 12:55:22 MDT 2006


Hey ho.

During my presentation last week, Dennis asked something along the lines 
of "what makes OCaml a better language for you?" Can't recall the exact 
words, but I believe that was the gist of it. I don't think I answered 
the question very well, so let me give y'all an example that might give 
you a better idea.

In reading this, consider the notion of programming *idioms*. After all, 
in terms of fundamental capabilities, most languages are pretty 
interchangeable. But the idioms that they support can have a huge effect 
on whether a given language is comfortable and productive for you. OCaml 
  supports a number of idioms that work well for me.

The following is a simplified version of a technique I thought of a 
couple of days ago (the first time in history anyone's thought of 
something like this, I'm sure ;-)). I think that this kind of thing can 
only be done elegantly in an impure functional language like OCaml. You 
could do something similar in Python, but it wouldn't be type-safe.


   PROBLEM:

     An image viewer has several modes of operation: fullscreen vs. not,
     slideshow vs. manual, etc. The user is allowed to switch between
     modes at runtime. Each time an image is displayed, it needs to be
     displayed in an appropriate manner for the current mode.

   CONVENTIONAL SOLUTION:

     Set up some flags to indicate the mode, and test them each time
     an image is displayed:

       let mode = {
         fullscreen = false;
         slideshow = false;
         interval = 1000;   (* milliseconds *)
       }

       ....

       let show_image img =
         if mode.slideshow && mode.fullscreen then
           [ do something ]
         else if mode.slideshow then
           [ do something else ]
         else if mode.fullscreen then
           [ something else again ]
         else
           [ etc. ]

   WHAT'S WRONG WITH THAT?

     1. Evaluating conditionals can affect performance (honestly, the
        effect is probably minuscule in this case, but it's not zero, and
        might be large in some cases).
     2. There is no clear mechanism for managing the mode, making bugs
        more likely and debugging harder.

     So-o-o:

   WAY-COOL, IMPURE FUNCTIONAL SOLUTION

     1. Create a type to represent mode transitions (this part is
        optional, but contributes to the clarity of your code):

          type transition =
            | FullscreenOn
            | FullscreenOff
            | SlideshowOn
            | SlideshowOff

        These symbols will be generated in response to keyboard or mouse
        events.

     2. Create the show_image function as a reference:

          let show_image = ref (fun img -> ())

        OCaml uses '()' for the value called 'unit', which is more or
        less like void in C. Since a function has to return a value,
        you use () when there is no interesting value to return. So the
        above function is a no-op. The 'ref' keyword means the value is
        a *reference* (similar to a 'pointer' in some other languages).
        Unlike normal variables in OCaml, references are mutable and can
        be assigned values.

     3. Create a "function factory" that returns a mode-specific
        function:

          let get_show_image mode =
            match mode.slideshow, mode.fullscreen with
            | true, true -> (fun img -> [ do something ])
            | true, false -> (fun img -> [ do something else ])
            | false, true -> (fun img -> [ something else again ])
            | false, false -> (fun img -> [ etc. ])

        So, depending on the current mode, the above function returns
        one or another *function* that does something with an image.

     4. Create a set_mode function that manages transitions:

          let set_mode transition =
            (* I'm not showing the transform_mode function, but the
               concept is self-explanatory, I hope. *)
            let new_mode = transform_mode current_mode transition in
            show_image := get_show_image new_mode
            (* ':=' is how you assign to a reference *)

     5. How to display an image:

          !show_image img
            (* '!' is used to dereference the reference, whose current
               value has been set by the 'set_mode' function *)

   So, what have we done? Well, now, instead of checking the mode every
   time we display an image, we check it *only* when it changes, and swap
   out our main display function accordingly. And when !show_image is
   called, it just does its thing with little or no evaluation of
   conditionals. Furthermore, there is only one function that can change
   the mode, so in addition to a possibly-tiny performance boost, we also
   gain a significant amount of clarity in how the program functions: if
   the current state is X, the current display function *must be x*. So
   when you need to debug, the logic looks something like this:

     1. Does the mode get changed appropriately?
        NO: fix the set_mode function.
        YES: go to (2).
     2. Is the show_image function for this mode correct?
        NO: fix it
        YES: Umm ... well, you get the idea.


I think that's pretty damn cool. No doubt it is overly elaborate for 
some situations. I think the distinction between "scripting languages" 
and "programming languages" is somewhat bogus, but to the extent that 
it's valid, OCaml is definitely the latter. So, right tool for the right 
job: if I needed a quick system administration script, I'd look at 
(e.g.) Python first, but for a moderately-to-highly complex desktop 
application, OCaml would be my first choice.

-- 
Matt Gushee
: Bantam - lightweight file manager : matt.gushee.net/software/bantam/ :
: RASCL's A Simple Configuration Language :     matt.gushee.net/rascl/ :



More information about the clue-talk mailing list