Search code examples
functional-programmingsmlml

'Unpacking' the data in an SML DataType without a case statement


I have an SML program which represents a language with Expressions that are comprised of Values:

datatype Value = IntVal of int
               | ListVal of Value list

datatype Exp = Const of Value
             | Plus of Exp * Exp
             | Minus of Exp * Exp
             | Times of Exp * Exp

I'm also writing an eval function that converts an expression into a value. If the expression is a Plus expression (e.g. Plus (Const (IntVal 1), Const (IntVal 1)) which represents 1+1), I just want to take out the integer stored in the IntVal and just add them together and return that.

But as far as I can tell, I have to have a seemingly redundant case statement with only one case just to get at the integer inside the IntVal data type:

(*Evaluates an Exp and returns a Value*)
fun eval e =
  (*Evaluate different types of Exp*)
  case e of
      (*If it's a constant, then just return the Value*)
      Const v => v
      (*If it's a Plus, we want to add together the two Values*)
    | Plus (x,y) =>
         (*Case statement with only one case that seems redundant*)
         case (eval x, eval y) of
            (IntVal xVal, IntVal yVal) => IntVal (xVal + yVal)

Is there no easy way to do simplify this? I'd like to do something like this, which of course isn't valid SML:

fun eval e =
  case e of
      Const v => v
    | Plus (x,y) => IntVal (eval x + eval x)

Solution

  • If you want your eval function to return an int and you haven't figured out how to get an int from a Value which uses the ListVal constructor -- it is enough to just supply patterns which correspond to the cases that your intended definition covers.

    fun eval (Const (IntVal v)) = v
    |   eval (Plus (e1,e2)) = eval(e1) + eval(e2)
    |   eval (Minus (e1,e2)) = eval(e1) - eval(e2)
    |   eval (Times (e1,e2)) = eval(e1) * eval(e2);
    

    SML/NJ gives Warning: match nonexhaustive - but if it matches your intention then you can ignore the warning.

    The above code returns an int. If you want to return values which look like e.g. IntVal 3 then you could define 3 functions which take pairs of IntVals and return IntVals corresponding to their sums, differences, and products and use these functions on the right hand sides of the above definition.