VINT64
VINT64

Reputation: 153

OCaml: inner "let" for two functions

I want to make inner "let", but for two functions.

I have now a function

let fresh_var () =
let r = ref 0 in
r := !r + 1 ; Var !r;;

I want to add second function so that it could change r, but r stays invisible for the rest of the program. Something like:

let r = ref 0 in 
let fresh_var () = r := !r + 1 ; Var !r
and let refresh () = r := 0

But piece above doesn't work because of syntax error.

How can I implement this idea in OCaml?

Upvotes: 2

Views: 1649

Answers (4)

Mulan
Mulan

Reputation: 135406

OCaml also supports a rich object-oriented system

class counter = object
  val mutable r = 0

  method value =
    r

  method incr =
    r <- r + 1;
    r

  method reset =
    r <- 0;
    0
end

We can use our counter like this

let () =
  let c = new counter in
  printf "counter value: %d\n" c#value;   (* counter value: 0 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 1 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 2 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 3 *)
  printf "counter value: %d\n" c#value;   (* counter value: 3 *)
  printf "counter value: %d\n" c#value;   (* counter value: 3 *)
  printf "counter value: %d\n" c#reset;   (* counter value: 0 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 1 *)

Class instances encapsulate their data members, so we can easily manage multiple counters

let () =
  let a = new counter in
  let b = new counter in
  printf "A: %d, B: %d\n" a#value b#value;  (* A: 0, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 1, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 2, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 3, B: 0 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 1 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 2 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 3 *)
  printf "A: %d, B: %d\n" a#reset b#reset;  (* A: 0, B: 0 *)

Upvotes: 0

vonaka
vonaka

Reputation: 953

You can simply create one function returning a pair of functions:

let get_fresh_and_reset () =
  let r = ref 0 in
  (fun () -> incr r; !r), (fun () -> r := 0)

let fresh, reset = get_fresh_and_reset ()

Also note that the correct syntax is := and not =:.

Edit:

As @Virgile mentioned, in case if you don't need several counters you can simplify:

let fresh_var, refresh =
    let r = ref 0 in (fun () -> incr r; !r), (fun () -> r:=0)

Upvotes: 3

Richard-Degenne
Richard-Degenne

Reputation: 2959

This is not how things work in OCaml. Local definitions are exactly that, local. They cannot be shared between functions.

If you want to abstract part of your implementation, I suggest using modules instead.

module Incrementer : sig
  val next : unit -> int
  val reset : unit -> unit
end = struct
  let r = ref 0

  let next () =
    r := !r + 1;
    !r

  let reset () =
    r := 0
end

See it in action below:

# Incrementer.next ();;
- : int = 1

# Incrementer.next ();;
- : int = 2

# Incrementer.reset ();;
- : unit = ()

# Incrementer.next ();;
- : int = 1

# Incrementer.r;;
Error: Unbound value Incrementer.r

Below is a better implementation which allows you to have multiple Incrementers at once.

module Incrementer : sig
  type t
  val create : unit -> t
  val next : t -> int
  val reset : t -> unit
end = struct
  type t = int ref

  let create () =
    ref 0

  let next t =
    t := !t + 1;
    !t

  let reset t =
    t := 0
end

Let's see it in action:

# let incrementer = Incrementer.create ();;
val incrementer : Incrementer.t = <abstr>
(* As you can see, the outer code never sees the `int ref` inside. *)

# Incrementer.next incrementer;;
- : int = 1

# Incrementer.next incrementer;;
- : int = 2

# Incrementer.reset incrementer;;
- : unit = ()

# Incrementer.next incrementer;;
- : int = 1

You can also write the signature and the implementation in separate files in order to compile them separately.

Upvotes: 2

Daiwen
Daiwen

Reputation: 727

and is used instead of let when you want to write joint declarations, and in is only used in local contexts:

let r = ref 0
let fresh_var () = r := !r + 1 ; Var !r
and refresh () = r := 0

However, since your functions are not codependent the use of and is not necessary here so you could use another let construct.

As for the idea you have, you would have to define these functions in a seperate module whose .mli only declares them and not the variable r.

Upvotes: 0

Related Questions