Skip to content

Commit d6c3fa8

Browse files
authored
Merge pull request #85 from jlouis/ocaml-tagless-final
Implement an ocaml variant based on tagless final
2 parents 02271c7 + ec93119 commit d6c3fa8

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

ocaml/tagless_final.ml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
(* Implementation using a final-tagless representation.
2+
* This is somewhat common among DSL-solutions in OCaml
3+
*)
4+
5+
module JSON = struct
6+
(* This part implements JSON output. A real solution is probably
7+
* using something like YoJson or likewise here. It serves as the output
8+
* type as well
9+
*)
10+
type t =
11+
| JString of string
12+
| JInt of int
13+
| JList of t list
14+
| JBool of bool
15+
| JDict of (string * t) list
16+
end
17+
18+
(* Define the data at the type-level as functions you compose together.
19+
*
20+
* Defining a parser from a concrete structure into these functions is an
21+
* exercise left for the reader.
22+
*)
23+
module type SYM = sig
24+
type out = JSON.t
25+
type 'a repr
26+
type lesson
27+
type chapter
28+
type db
29+
val lesson : string -> lesson repr
30+
val chapter : string -> bool -> (lesson repr) list -> chapter repr
31+
val db: (chapter repr) list -> db repr
32+
33+
val eval : db repr -> out
34+
end
35+
36+
(* The example, given as abstract data. Note that we define this as a functor,
37+
* so we don't say how the functions db, chapter, lesson, ... are evaluated
38+
*)
39+
module Ex1(S: SYM) = struct
40+
open S
41+
let ex1 = db [chapter "Getting started" false [lesson "Welcome"; lesson "Installation"];
42+
chapter "Basic operator" false [lesson "Addition / Subtraction"; lesson "Multiplication / Division"];
43+
chapter "Advanced topics" true [lesson "Mutability"; lesson "Immutability"]]
44+
let ex1_eval = eval ex1
45+
end
46+
47+
(* Provide an evaluation interpreter over the structure *)
48+
module Eval : SYM = struct
49+
type out = JSON.t
50+
(* The representation here is key. It states we thread the positional arguments around.
51+
* A more CPS-like variant are also possible here by manipulating this type *)
52+
type 'a repr = int -> int -> (int * int * 'a)
53+
type lesson = JSON.t
54+
type chapter = JSON.t
55+
type db = JSON.t
56+
57+
let lesson l i j = (i, j+1, JSON.JDict [("name", JString l); ("position", JInt j)])
58+
59+
(* Unexported helper for a list of lessons *)
60+
let lessons (ls: lesson repr list) i j =
61+
let (final_i, final_j, xs) =
62+
(List.fold_left
63+
(fun (i, j, acc) less -> let (i', j', out) = less i j in (i', j', out::acc))
64+
(i,j, [])
65+
ls)
66+
in (final_i, final_j, JSON.JList (List.rev xs))
67+
68+
(* The following two functions make aggressive use of binding shadowing. It's intended *)
69+
let chapter title reset ls i j =
70+
let j = if reset then 1 else j in
71+
let (i, j, less_out) = lessons ls i j
72+
in (i+1, j, JSON.JDict [("title", JString title); ("position", JInt i); ("reset_lesson_position", JBool reset); ("lessons", less_out)])
73+
74+
let db (chapters: (chapter repr) list) i j =
75+
let rec loop cs i j acc =
76+
match cs with
77+
| [] -> (i, j, List.rev acc)
78+
| c::next ->
79+
let (i, j, d) = c i j in loop next i j (d::acc) in
80+
let (i, j, xs) = loop chapters i j []
81+
in (i, j, JSON.JList xs)
82+
83+
let eval db =
84+
let (_, _, res) = db 1 1 in res
85+
end
86+

0 commit comments

Comments
 (0)