Extending immutable types (or: fast cache for immutable types) in OCaml

I have a recursive immutable data structure in ocaml that can be simplified something like this:

type expr =
{
    eexpr : expr_expr;
    some_other_complex_field : a_complex_type;
}

and expr_expr =
    | TInt of int
    | TSum of (expr * expr)
    | TMul of (expr * expr)

This is AST, and sometimes it gets quite complicated (it is very deep).

There is a recursive function that evaluates an expression. For example, let's say

let rec result expr =
    match expr.eexpr with
        | TInt i -> i
        | TSum (e1, e2) -> result e1 + result e2
        | TMul (e1, e2) -> result e1 * result e2

Now suppose I match an expression with another expression, and I need to constantly check the result of expr, sometimes more than once for the same expr, and sometimes for expressions that were recently displayed using the template

{ someExpr with eexpr = TSum(someExpr, otherExpr) }

, , . , Hashtbl, AFAIK Hashtbl , . , "" expr. .

, Ocaml , , ?

!

+5
3

, -. , (==) ; .. A == B, f A = f B f. , f A. , B, A, B.

(==) , - -, . , . .

- , .

, , , .

+4

- expr_expr. , , (=) (==).

-consing OCaml.

+5

, : -consing-like, " " memoization eval .

Of course, this only works when your function evalreally depends only on a part of the "pure expression" of the function, as in the example you specified. I believe that this is a relatively common case, at least if you limit yourself to storing successful ratings (this, for example, will not lead to an error, including some location information).

Edit: little proof of concept:

type 'a _expr =
  | Int of int
  | Add of 'a * 'a

(* a constructor to avoid needing -rectypes *)
type pure_expr = Pure of pure_expr _expr

type loc = int
type loc_expr = {
  loc : loc;
  expr : loc_expr _expr;
  pure : pure_expr (* or any hash_consing of it for efficiency *)
}

(* this is where you could hash-cons *)
let pure x = Pure x

let int loc n =
  { loc; expr = Int n; pure = pure (Int n) }
let add loc a b =
  { loc; expr = Add (a, b); pure = pure (Add(a.pure, b.pure)) }

let eval =
  let cache = Hashtbl.create 251 in
  let rec eval term =
    (* for debug and checking memoization *)
    Printf.printf "log: %d\n" term.loc;
    try Hashtbl.find cache term.pure with Not_found ->
      let result =
        match term.expr with
          | Int n -> n
          | Add(a, b) -> eval a + eval b in
      Hashtbl.add cache term.pure result;
      result
  in eval



let test = add 3 (int 1 1) (int 2 2)
# eval test;;
log: 3
log: 2
log: 1
- : int = 3
# eval test;;
log: 3
- : int = 3
+1
source

All Articles