Recursive anonymous functions in SML

2019-04-22 02:49发布

问题:

Is it possible to write recursive anonymous functions in SML? I know I could just use the fun syntax, but I'm curious.

I have written, as an example of what I want:

val fact =
    fn n => case n of
                 0 => 1
               | x => x * fact (n - 1)

回答1:

The anonymous function aren't really anonymous anymore when you bind it to a variable. And since val rec is just the derived form of fun with no difference other than appearance, you could just as well have written it using the fun syntax. Also you can do pattern matching in fn expressions as well as in case, as cases are derived from fn.

So in all its simpleness you could have written your function as

val rec fact = fn 0 => 1
                | x => x * fact (x - 1)

but this is the exact same as the below more readable (in my oppinion)

fun fact 0 = 1
  | fact x = x * fact (x - 1)

As far as I think, there is only one reason to use write your code using the long val rec, and that is because you can easier annotate your code with comments and forced types. For examples if you have seen Haskell code before and like the way they type annotate their functions, you could write it something like this

val rec fact : int -> int =
fn 0 => 1
 | x => x * fact (x - 1)

As templatetypedef mentioned, it is possible to do it using a fixed-point combinator. Such a combinator might look like

fun Y f =
    let
      exception BlackHole
      val r = ref (fn _ => raise BlackHole)
      fun a x = !r x
      fun ta f = (r := f ; f)
    in
      ta (f a)
    end

And you could then calculate fact 5 with the below code, which uses anonymous functions to express the faculty function and then binds the result of the computation to res.

val res =
    Y (fn fact =>
       fn 0 => 1
        | n => n * fact (n - 1)
      )
      5                       

The fixed-point code and example computation are courtesy of Morten Brøns-Pedersen.


Updated response to George Kangas' answer:

In languages I know, a recursive function will always get bound to a name. The convenient and conventional way is provided by keywords like "define", or "let", or "letrec",...

Trivially true by definition. If the function (recursive or not) wasn't bound to a name it would be anonymous.

The unconventional, more anonymous looking, way is by lambda binding.

I don't see what unconventional there is about anonymous functions, they are used all the time in SML, infact in any functional language. Its even starting to show up in more and more imperative languages as well.

Jesper Reenberg's answer shows lambda binding; the "anonymous" function gets bound to the names "f" and "fact" by lambdas (called "fn" in SML).

The anonymous function is in fact anonymous (not "anonymous" -- no quotes), and yes of course it will get bound in the scope of what ever function it is passed onto as an argument. In any other cases the language would be totally useless. The exact same thing happens when calling map (fn x => x) [.....], in this case the anonymous identity function, is still in fact anonymous.

The "normal" definition of an anonymous function (at least according to wikipedia), saying that it must not be bound to an identifier, is a bit weak and ought to include the implicit statement "in the current environment".

This is in fact true for my example, as seen by running it in mlton with the -show-basis argument on an file containing only fun Y ... and the val res ..

val Y: (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b
val res: int32

From this it is seen that none of the anonymous functions are bound in the environment.

A shorter "lambdanonymous" alternative, which requires OCaml launched by "ocaml -rectypes":

(fun f n -> f f n) 
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;; Which produces 7! = 5040.

It seems that you have completely misunderstood the idea of the original question:

Is it possible to write recursive anonymous functions in SML?

And the simple answer is yes. The complex answer is (among others?) an example of this done using a fix point combinator, not a "lambdanonymous" (what ever that is supposed to mean) example done in another language using features not even remotely possible in SML.



回答2:

All you have to do is put rec after val, as in

val rec fact =
        fn n => case n of
                     0 => 1
                   | x => x * fact (n - 1)

Wikipedia describes this near the top of the first section.



回答3:

let fun fact 0 = 1
      | fact x = x * fact (x - 1)
in
  fact
end

This is a recursive anonymous function. The name 'fact' is only used internally.

Some languages (such as Coq) use 'fix' as the primitive for recursive functions, while some languages (such as SML) use recursive-let as the primitive. These two primitives can encode each other:

fix f => e   
  := let rec f = e in f end

let rec f = e ... in ... end 
  := let f = fix f => e ... in ... end 


回答4:

In languages I know, a recursive function will always get bound to a name. The convenient and conventional way is provided by keywords like "define", or "let", or "letrec",...

The unconventional, more anonymous looking, way is by lambda binding. Jesper Reenberg's answer shows lambda binding; the "anonymous" function gets bound to the names "f" and "fact" by lambdas (called "fn" in SML).

A shorter "lambdanonymous" alternative, which requires OCaml launched by "ocaml -rectypes":

(fun f n -> f f n)
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;;

Which produces 7! = 5040.