Search code examples
constantssmlpolyml

In SML why aren't you allowed real constant in pattern?


This code is not accepted;

> fun fact 0.0 = 1.0
Error-Real constants not allowed in patterns
> | fact n = n*fact(n-1);
Static Errors

Why is this?


Solution

  • real is not an equality type. SML places a premium on producing provably correct code. Comparing two real numbers for equality is more often than not a bad idea since it might be the case that x = y mathematically but, because of round-off error, x != y at run-time. This is a notorious source of bugs in naïve implementations of numerical algorithms. Since it is so often such a bad idea, SML simply bans it. Since it thus impossible to compare an input for equality with the pattern 1.0, it wouldn't make sense to allow it as a pattern.

    In the relatively few cases that you really want to compare two reals for equality, you can use x <= y andalso x => y. Alternatively (as @AndreasRossberg points out), one can use the standard library function Real.== which is used like Real.==(x,y). This last looks a bit odd, so you can declare it as an infix operator:

    val ==  = Real.==
    infix 4 ==
    

    and then simply x == y Unfortunately neither of these can be turned into a pattern, although they do make it possible to write:

    fun fact x = if x == 0.0 then 1.0 else x * fact(x-1.0) 
    

    which works as possibly intended. On the other hand, as @SimonShine points out, this will crash if you feed it any input which isn't of the form n.0 where n is an int (even if this is only due to round-off error). This is precisely the sort of problem that the creators of SML was trying to prevent. It makes much more sense to define fact to take and return ints:

    fun fact x = if x = 0 then 1 else x * fact(x-1)
    

    (or -- read up on the gamma function if you really want a floating-point factorial).

    This last definition can easily be converted into the pattern-matching form that you seemed to be trying for.