2

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?

sshine
  • 15,635
  • 1
  • 41
  • 66
  • 1
    See the "Comparing `real`s" part of [this answer](http://stackoverflow.com/questions/40601391/syntax-error-in-creating-a-list-from-a-tree-in-sml/40604755#40604755). – sshine Dec 08 '16 at 16:44

1 Answers1

5

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.

John Coleman
  • 51,337
  • 7
  • 54
  • 119
  • 2
    It may be worth noting that you also have `Real.==` if you explicitly want it. – Andreas Rossberg Dec 08 '16 at 17:03
  • @AndreasRossberg Thank you for pointing that out. I've never had occasion to use that and had forgotten that it even exists. I added it to the answer. – John Coleman Dec 08 '16 at 17:31
  • 2
    I'm not sure how `fun fact x = if x == 0.0 then ...` is intended to work, but if, for some reason, the input `x` is rounded the wrong way, that function will throw `Out_of_memory`. Floats should be [compared properly](http://floating-point-gui.de/errors/comparison/). :-) – sshine Dec 08 '16 at 21:36
  • 1
    @SimonShine I didn't say that the intentions are good -- but if you intend to write a function which will compute the factorial of real numbers with 0 after the decimal point then that is how you would do it. This is essentially what you would do with JavaScript, which doesn't have a separate int type but just uses 64 bit floats for all numbers. Still, I should clarify. – John Coleman Dec 08 '16 at 21:54
  • @JohnColeman: Thanks, didn't know about the [gamma function](https://en.wikipedia.org/wiki/Gamma_function). – sshine Dec 08 '16 at 22:45
  • Apparently there's a [Math.Gamma](https://hackage.haskell.org/package/gamma-0.9.0.2/docs/Math-Gamma.html) Haskell package. – sshine Dec 08 '16 at 22:51
  • I think the bigger reason that `real` no longer admits equality in SML '97 is that IEEE 754, which defines floating-point numbers, defines their equality in an inconsistent way; for example, if `x` is NaN, then `Real.==(x, x)` is false! (SML '90 didn't specify how `=` would behave when given NaN, and didn't offer a separate primitive for IEEE 754 floating-point equality, so it wouldn't surprise me if some implementations handled it one way, and some implementations the other.) – ruakh Dec 18 '16 at 17:55