I am wondering how F# implements let rec, and I couldn't find an answer. As a preface, I'll address how Scheme implements letrec:
In Scheme, let is just syntactics sugar for a definition of a lambda and applying it:
(let ((x 1)) (+ x 2))
is transformed to
((lambda (x) (+ x 2)) 1)
(in each case the expression is evaluated to 3).
letrec is also syntactic sugar, but #f is passed as initial argument to the lambda's parameters, and set! expressions are injected before the letrec body, like in this transformation:
(letrec ((x 1)) (+ x 2)) => ((lambda (x) (begin (set! x 1) (+ x 2))) #f).
Considering that F# doesn't have an equivalent operator to Scheme's set!, how does it implement let rec? Does it declare the function's parameters as mutable, and then mutate them in the function's body?
In F#, let rec allows a reference to the binding from within the function before it has been bound. let rec doesn't have an implementation per se, because it is merely a compiler hint.
In this contrived example,
let rec even =
function 0 -> true | 1 -> false | x -> odd (x - 1)
and odd =
function 0 -> false | 1 -> true | x -> even (x - 1)
the compiled IL very unglamorously translates to:
public static bool even(int _arg1)
{
switch (_arg1)
{
case 0:
return true;
case 1:
return false;
default:
return odd(_arg1 - 1);
}
}
public static bool odd(int _arg2)
{
switch (_arg2)
{
case 0:
return false;
case 1:
return true;
default:
return even(_arg2 - 1);
}
}
All function definitions are statically compiled to IL.
F# ultimately is a language which runs on the CLR.
There is no meta-programming.
Related
Consider a function g(x):
g(x) = x == 0 ? 0 : g(g(x - 1))
I don't think this is tail recursive, since the call to g(x) can be broken down into two parts:
let y = g(x - 1);
return g(y);
how to convert this to tail recursion?
continuation-passing style
You can convert from direct style to continuation-passing style -
g'(x,return) =
x == 0
? return(0)
: g'(x - 1, x' => # tail
g'(x', return)) # tail
Now write g to call g' with the default continuation -
g(x) = g'(x, x' => x')
Depending on the language you use, you may have to rename return to something else. k is a popular choice.
another example
We can see this technique applied to other problems like fib -
# direct style
fib(x) =
x < 2
? x
: fib(x - 1) + fib(x - 2) # "+" has tail position; not fib
# continuation-passing style
fib'(x, return) =
x < 2
? return(x)
: fib'(x - 1, x' => # tail| first compute x - 1
fib(x - 2, x'' => # tail| then compute x - 2
return(x' + x''))) # tail| then return sum
fib(x) = fib'(x, z => z)
other uses
Continuation-passing style is useful in other ways too. For example, it can be used to provide branching or early-exit behavior in this search program -
search(t, index, match, ifFound, notFound) =
i >= length(t)
? notFound()
: t[index] == match
? ifFound(index)
: search(t, index + 1, match, ifFound, notFound)
We can call search with continuations for each possible outcome -
search(["bird", "cat", "dog"],
0,
"cat",
matchedIndex => print("found at: " + matchedIndex),
() => print("not found")
)
how to
A function written in continuation-passing style takes an extra argument: an explicit "continuation"; i.e., a function of one argument — wikipedia
(* direct style *)
add(a, b) = ...
(* continuation-passing style takes extra argument *)
add(a, b, k) = ...
When the CPS function has computed its result value, it "returns" it by calling the continuation function with this value as the argument.
(* direct style *)
add(a, b) =
a + b
(* continuation-passing style "returns" by calling continuation *)
add(a, b, k) =
k(a + b) (* call k with the return value *)
That means that when invoking a CPS function, the calling function is required to supply a procedure to be invoked with the subroutine's "return" value.
(* direct style *)
print(add(5, 3)) (* 8 *)
(* continuation-passing style *)
add(5, 3, print) (* 8 *)
Expressing code in this form makes a number of things explicit which are implicit in direct style. These include: procedure returns, which become apparent as calls to a continuation; intermediate values, which are all given names; order of argument evaluation, which is made explicit; and tail calls, which simply call a procedure with the same continuation, unmodified, that was passed to the caller.
you've probably used continuations before
If you've ever run into someone saying "callback", what they really mean is continuation.
"When the button is clicked, continue the program with event => ..." -
document.querySelector("#foo").addEventListener("click", event => {
console.log("this code runs inside a continuation!")
})
<button id="foo">click me</button>
"When the file contents are read, continue the program with (err, data) => ..." -
import { readFile } from 'fs';
readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
There seems to be syntactical restrictions within SML's recursive bindings, which I'm unable to understand. What are these restrictions I'm not encountering in the second case (see source below) and I'm encountering when using a custom operator in the first case?
Below is the case with which I encountered the issue. It fails when I want to use a custom operator, as explained in comments. Of the major SML implementations I'm testing SML sources with, only Poly/ML accepts it as valid, and all of MLton, ML Kit and HaMLet rejects it.
Error messages are rather confusing to me. The clearest one to my eyes, is the one from HaMLet, which complains about “illegal expression within recursive value binding”.
(* A datatype to pass recursion as result and to arguments. *)
datatype 'a process = Chain of ('a -> 'a process)
(* A controlling iterator, where the item handler is
* of type `'a -> 'a process`, so that while handling an item,
* it's also able to return the next handler to be used, making
* the handler less passive. *)
val rec iter =
fn process: int -> int process =>
fn first: int =>
fn last: int =>
let
val rec step =
fn (i: int, Chain process) (* -> unit *) =>
if i < first then ()
else if i = last then (process i; ())
else if i > last then ()
else
let val Chain process = process i
in step (i + 1, Chain process)
end
in step (first, Chain process)
end
(* An attempt to set‑up a syntax to make use of the `Chain` constructor,
* a bit more convenient and readable. *)
val chain: unit * ('a -> 'a process) -> 'a process =
fn (a, b) => (a; Chain b)
infixr 0 THEN
val op THEN = chain
(* A test of this syntax:
* - OK with Poly/ML, which displays “0-2|4-6|8-10|12-14|16-18|20”.
* - fails with MLton, which complains about a syntax error on line #44.
* - fails with ML Kit, which complains about a syntax error on line #51.
* - fails with HaMLet, which complains about a syntax error on line #45.
* The clearest (while not helpful to me) message comes from HaMLet, which
* says “illegal expression within recursive value binding”. *)
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
val () = iter process 0 20
val () = print "\n"
(* Here is the same without the `THEN` operator. This one works with
* all of Poly/ML, MLton, ML Kit and HaMLet. *)
val rec process =
fn x =>
(print (Int.toString x);
Chain (fn x => (print "-";
Chain (fn x => (print (Int.toString x);
Chain (fn x => (print "|";
Chain process)))))))
val () = iter process 0 20
val () = print "\n"
(* SML implementations version notes:
* - MLton, is the last version, built just yesterday
* - Poly/ML is Poly/ML 5.5.2
* - ML Kit is MLKit 4.3.7
* - HaMLet is HaMLet 2.0.0 *)
Update
I could work around the issue, but still don't understand it. If I remove the outermost parentheses, then it validates:
val rec process: int -> int process =
fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process)))
Instead of:
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
But why is this so? An SML syntax subtlety? What's its rational?
It's just an over-restrictive sentence in the language definition, which says:
For each value binding "pat = exp" within rec, exp must be of the form "fn match".
Strictly speaking, that doesn't allow any parentheses. In practice, that's rarely a problem, because you almost always use the fun declaration syntax anyway.
I defined a higher-order function like this:
val func : int -> string -> unit
I would like to use this function in two ways:
other_func (func 5)
some_other_func (fun x -> func x "abc")
i.e., by making functions with one of the arguments already defined. However, the second usage is less concise and readable than the first one. Is there a more readable way to pass the second argument to make another function?
In Haskell, there's a function flip for this. You can define it yourself:
let flip f x y = f y x
Then you can say:
other_func (func 5)
third_func (flip func "abc")
Flip is defined in Jane Street Core as Fn.flip. It's defined in OCaml Batteries Included as BatPervasives.flip. (In other words, everybody agrees this is a useful function.)
The question posed in the headline "change order of parameters" is already answered. But I am reading your description as "how do I write a new function with the second parameter fixed". So I will answer this simple question with an ocaml toplevel protocol:
# let func i s = if i < 1 then print_endline "Counter error."
else for ix = 1 to i do print_endline s done;;
val func : int -> string -> unit = <fun>
# func 3 "hi";;
hi
hi
hi
- : unit = ()
# let f1 n = func n "curried second param";;
val f1 : int -> unit = <fun>
# f1 4;;
curried second param
curried second param
curried second param
curried second param
- : unit = ()
#
I'm new to ocaml and tryin to write a continuation passing style function but quite confused what value i need to pass into additional argument on k
for example, I can write a recursive function that returns true if all elements of the list is even, otherwise false.
so its like
let rec even list = ....
on CPS, i know i need to add one argument to pass function
so like
let rec evenk list k = ....
but I have no clue how to deal with this k and how does this exactly work
for example for this even function, environment looks like
val evenk : int list -> (bool -> ’a) -> ’a = <fun>
evenk [4; 2; 12; 5; 6] (fun x -> x) (* output should give false *)
The continuation k is a function that takes the result from evenk and performs "the rest of the computation" and produces the "answer". What type the answer has and what you mean by "the rest of the computation" depends on what you are using CPS for. CPS is generally not an end in itself but is done with some purpose in mind. For example, in CPS form it is very easy to implement control operators or to optimize tail calls. Without knowing what you are trying to accomplish, it's hard to answer your question.
For what it is worth, if you are simply trying to convert from direct style to continuation-passing style, and all you care about is the value of the answer, passing the identity function as the continuation is about right.
A good next step would be to implement evenk using CPS. I'll do a simpler example.
If I have the direct-style function
let muladd x i n = x + i * n
and if I assume CPS primitives mulk and addk, I can write
let muladdk x i n k =
let k' product = addk x product k in
mulk i n k'
And you'll see that the mulptiplication is done first, then it "continues" with k', which does the add, and finally that continues with k, which returns to the caller. The key idea is that within the body of muladdk I allocated a fresh continuation k' which stands for an intermediate point in the multiply-add function. To make your evenk work you will have to allocate at least one such continuation.
I hope this helps.
Whenever I've played with CPS, the thing passed to the continuation is just the thing you would normally return to the caller. In this simple case, a nice "intuition lubricant" is to name the continuation "return".
let rec even list return =
if List.length list = 0
then return true
else if List.hd list mod 2 = 1
then return false
else even (List.tl list) return;;
let id = fun x -> x;;
Example usage: "even [2; 4; 6; 8] id;;".
Since you have the invocation of evenk correct (with the identity function - effectively converting the continuation-passing-style back to normal style), I assume that the difficulty is in defining evenk.
k is the continuation function representing the rest of the computation and producing a final value, as Norman said. So, what you need to do is compute the result of v of even and pass that result to k, returning k v rather than just v.
You want to give as input the result of your function as if it were not written with continuation passing style.
Here is your function which tests whether a list has only even integers:
(* val even_list : int list -> bool *)
let even_list input = List.for_all (fun x -> x mod 2=0) input
Now let's write it with a continuation cont:
(* val evenk : int list -> (bool -> 'a) -> 'a *)
let evenk input cont =
let result = even_list input in
(cont result)
You compute the result your function, and pass resultto the continuation ...
For an assignment, i have written the following code in recursion. It takes a list of a vector data type, and a vector and calculates to closeness of the two vectors. This method works fine, but i don't know how to do the recursive version.
let romulus_iter (x:vector list) (vec:vector) =
let vector_close_hash = Hashtbl.create 10 in
let prevkey = ref 10000.0 in (* Define previous key to be a large value since we intially want to set closefactor to prev key*)
if List.length x = 0 then
{a=0.;b=0.}
else
begin
Hashtbl.clear vector_close_hash;
for i = 0 to (List.length x)-1 do
let vecinquestion = {a=(List.nth x i).a;b=(List.nth x i).b} in
let closefactor = vec_close vecinquestion vec in
if (closefactor < !prevkey) then
begin
prevkey := closefactor;
Hashtbl.add vector_close_hash closefactor vecinquestion
end
done;
Hashtbl.find vector_close_hash !prevkey
end;;
The general recursive equivalent of
for i = 0 to (List.length x)-1 do
f (List.nth x i)
done
is this:
let rec loop = function
| x::xs -> f x; loop xs
| [] -> ()
Note that just like a for-loop, this function only returns unit, though you can define a similar recursive function that returns a meaningful value (and in fact that's what most do). You can also use List.iter, which is meant just for this situation where you're applying an impure function that doesn't return anything meaningful to each item in the list:
List.iter f x