Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement the Russian doll pattern in Ocaml?

Tags:

ocaml

In Javascript there is a pattern called the Russian doll pattern (this may also be called a 'one-shot'). Basically, it's a function that replaces itself with another at some point.

Simple example:

var func = function(){ 
  func = function(){ console.log("subsequent calls call this...");};
  console.log("first call");
}

So the first time you call func it'll output "first call" and the next (and subsequent times) it's print "subsequent calls call this...". (this would be easy to do in Scheme as well, for example)

I've been puzzling on how to do this in Ocaml?

Edit: one solution I've come up with:

 let rec func = ref( fun () -> func := ( fun () -> Printf.printf("subsequent..\n"));Printf.printf("First..\n"));;

Called as: !func () ;;

Interestingly, if I do not include the 'rec' in the definition, it never calls the subsequent function... It always prints 'First...'.

like image 959
aneccodeal Avatar asked Mar 20 '11 19:03

aneccodeal


2 Answers

yzzlr answer is very good, but two remarks:

It forces the input of the functions to be of type unit. You can use a polymorphic version:

let doll f1 f2 =
  let rec f = ref (fun x -> f := f2; f1 x) in
  (fun x -> !f x);;

You can do without the hairy recursion:

let doll f1 f2 =
  let f = ref f1 in
  f := (fun x -> f := f2; f1 x);
  (fun x -> !f x);;

(Replacing recursion with mutation is a common trick; it can actually be used to define fixpoints without using "rec")

like image 79
gasche Avatar answered Oct 15 '22 23:10

gasche


It's pretty straightforward, but you need to use side-effects. Here's a function that takes two thunks as arguments, and returns a new thunk that calls the first thunk the first time, and the second thunk every other time.

let doll f1 f2 =
   let f = ref f1 in
   (fun () ->
      let g = !f in
      f := f2;
      g ())

This isn't quite optimal, because we'll keep on overwriting the ref with the same value over and over.

Here's a slightly better version, which uses a recursive definition.

let doll f1 f2 =
   let rec f = ref (fun () -> f := f2;f1 ()) in
   (fun () -> !f ())

So, now, you'll get this:

# let f = doll (fun () -> 1) (fun () -> 2);;
val f : unit -> int = <fun>
# f ();;
- : int = 1
# f ();;
- : int = 2
like image 38
yzzlr Avatar answered Oct 16 '22 00:10

yzzlr