Reputation: 8806
In SML, I've been taught the idiomatic way to define a variable local to the function as:
fun correct_fun() =
let val x = 1
in x + 2
end
Why do I have to use let
, and not just val
like so:
fun incorrect_fun() =
val x = 1
x + 2
The incorrect_fun()
throws an error but I don't understand why. Why can't val be used inside a function without let
?
Upvotes: 1
Views: 2740
Reputation: 16105
Why can't
val
be used inside a function withoutlet
?
Because val
is a kind of declaration, let
is a kind of expression, and a function body is an expression.
The syntactic structure of a let
-expression is let
dec
in
exp
end
. So in using a let
-expression as the function body, the exp
within the let
is equivalent to the function body, but with an extended, local scope of whatever dec
adds.
The let
-expression lets you use any kind of declaration, not just val
declarations.
For example, you can use exception handling as a control-flow mechanism used for backtracking, and you can nest helper functions that are only used locally and possibly takes multiple arguments for storing a temporary result, but you may not want to expose the exception or the helper functions. So for the Eight Queens puzzle, you might refine this solution (from supplemental notes on functional programming, pp. 140-143, by Niels Andersen):
fun concatMap f xs = String.concat (List.map f xs)
fun concatTab f n = String.concat (List.tabulate (n, f))
fun dots n = concatTab (fn _ => ". ") n
fun show ys = concatMap (fn y => dots (y - 1) ^ "* " ^ dots (8 - y) ^ "\n") ys
fun queen dims =
let exception Queen
fun beats ((x,y),(x1,y1)) = (* x = x1 *)
(* orelse *) y = y1
orelse x + y = x1 + y1
orelse x - y = x1 - y1
fun safe ((x, y), _, []) = true
| safe ((x, y), x1, y1::ys) =
not (beats ((x, y), (x1, y1))) andalso safe ((x, y), x1 + 1, ys)
fun queen' ((0, _), ys) = ys
| queen' ((_, 0), _) = raise Queen
| queen' ((x, y), ys) =
if safe ((x, y), x + 1, ys)
then queen' ((x - 1, 8), y :: ys)
handle Queen => queen' ((x, y - 1), ys)
else queen' ((x, y - 1), ys)
in queen' (dims, []) end
Demonstrating it;
- print (show (queen ((8,8))));
. . . . * . . .
. . . . . . * .
. * . . . . . .
. . . . . * . .
. . * . . . . .
* . . . . . . .
. . . * . . . .
. . . . . . . *
When you're using let
-expressions to mainly declare temporary values, you could also consider going with a case-of
. See the Q&A's for Difference between "local" and "let" in SML and nested local declarations in ML of NJ for that.
Upvotes: 4