Search code examples
functionsmllookup-tables

SML function as lookup table


While delving into types in SML I found this

fun monthI2S 1 = "January"
  | monthI2S 2 = "February"
  | monthI2S 3 = "March"
  | monthI2S 4 = "April"
  | monthI2S 5 = "May"
  | monthI2S 6 = "June"
  | monthI2S 7 = "July"
  | monthI2S 8 = "August"
  | monthI2S 9 = "September"
  | monthI2S 10 = "October"
  | monthI2S 11 = "November"
  | monthI2S 12 = "December"

which to me looks like a function that serves as a lookup table. (I'm not even sure what this style of SML function is called, actually. It looks like a Haskell induction formula?) Would there be a way to not get the "non-exhaustive" warning by taking care of cases where the input is <= 0 orelse > 12?

fun monthI2S m = if m <= 0 orelse M > 12 then NONE else ...?
  | monthI2S 1 = "January"
  | monthI2S 2 = "February"
  | monthI2S 3 = "March"
  | monthI2S 4 = "April"
...

obviously doesn't work because I can't reconcile the else with the | .... Or am I just barking up the wrong tree entirely and should just use a case-like approach?


Solution

  • Pattern-matching proceeds from the topmost clause towards the bottom, so you want the most general case last.

    fun monthI2S 1 = "January"
      | monthI2S 2 = "February"
      ...
      | monthI2S m = nothing else matched, so m must be < 1 or > 12...
    

    Now, how you handle the error is up to you.

    The trivial and least robust solution is to just return "Invalid month number" or something like that.

    You could also raise an exception, or use an Option.

    If you use the latter, I would personally rewrite with a local function in order to avoid sprinkling SOME all over the place:

    fun monthI2S m = let
        fun monthI2S_safe 1 = "January"
          | monthI2S_safe 2 = "February"
          | monthI2S_safe 3 = "March"
          | monthI2S_safe 4 = "April"
          | monthI2S_safe 5 = "May"
          | monthI2S_safe 6 = "June"
          | monthI2S_safe 7 = "July"
          | monthI2S_safe 8 = "August"
          | monthI2S_safe 9 = "September"
          | monthI2S_safe 10 = "October"
          | monthI2S_safe 11 = "November"
          | monthI2S_safe 12 = "December"
          | monthI2S_safe _ = "Can never happen" 
    in
        if m >= 1 andalso m <= 12 then SOME (monthI2S_safe m) else NONE
    end;