Search code examples
pattern-matchingsml

Pattern matching in SML?


I'm just wondering what is XL variable here, it's not declared anywhere? This function returns NONE if a string is not in the list. Otherwise, returns the original string list but without the string matched.

fun same_string(s1 : string, s2 : string) =
    s1 = s2

fun all_except_option(s1: string, lst: string list) =
    case lst of
         [] => NONE
       | x::xl => if same_string(s1, x)    (* XL HERE ?? *)
                  then SOME xl
                  else case all_except_option(s1, xl) of
                            NONE => NONE
                          | SOME l => SOME (x::l)

Solution

  • As melpomene commented above, the xl on that line actually is the declaration of xl.

    In Standard ML, variables are declared (bound) by using patterns that are then matched to values. This is true even when using val; for example, we can write:

    val (x, y) = (1, "y")
    

    to declare x (binding it to 1) and y (binding it to "y").

    In case expressions and fn expressions, the pattern match also doubles as a conditional; for example, this:

    val rec sum =
      fn nil => 0
       | h :: t => h + sum t
    

    creates a function that checks if its argument is nil, in which case it does one thing, vs. if its argument has the form value1 :: value2 (meaning a non-empty list), in which case it does a different thing (using those values). (Hypothetically, if the argument were to have neither of those forms, then the function would end up raising Match; but as it happens, any value of type int list will perforce have one of those two forms, so the function will never raise Match.)

    Incidentally, note that that example uses the value identifiers nil and ::, but does not declare them; whereas it does declare the value identifiers h and t. This inconsistency may seem strange; the reason for it is that nil and :: are value constructors, meaning that they've been declared using datatype (datatype 'a list = nil | :: of 'a * 'a list), so they can be used in patterns. Something like this:

    val nil = 3
    

    is actually completely illegal, because nil refers to the constructor that's already been declared, and the types of nil and 3 are incompatible. By contrast, if an identifier is not already a value constructor, then appearance in a pattern constitutes a declaration (even if the identifier already is a value variable: the new declaration will simply hide the existing one). This can be a bit tricky for newcomers to the language, but with a little experience you'll find that it makes a lot of sense.