The module Map.Make is a functor, it cannot have any components - ocaml

I want to have a Map datastructure for "env" in my lisp interpreter.
My code in env.mli is
module Env = Map.Make(String)
type t = (Ast.expression) Env.t
val empty: t
val add: string -> Ast.expression -> t -> t
val lookup: string -> t -> Ast.expression
But my code in env.ml is:
module Env = Map.Make(String)
type t = (Ast.expression) Env.t
let (empty: t) = Env.empty
let add (id: string) (value: Ast.expression) (env: t): t = Env.add id (value) env
let lookup (id: string) (env: t) : Ast.expression = Env.find id env
And there aren't any error prompt. Curious about why and how to modify my code.
Quite new to functors and maybe I didn't fully understand it.
Thank you.

Based on the little information you provide, my guess is that what you want is to expose the Env fully as a map over strings. If so, you should replace
module Env = Map.Make(String)
with
module Env : Map.S with type key = string
but you could also leave the key type abstract:
module Env : Map.S
or expose only the type of the map:
module Env : sig
type 'a env
type t = e env
...
end = struct
module Env = Map.Make(String)
type 'a env = 'a Env.t
type t = Ast.expression Env.t
...
end
or you could make t itself abstract, to avoid referencing the env/map type at all:
module Env : sig
type t
...
end = struct
module Env = Map.Make(String)
type t = Ast.expression Env.t
...
end
There's many possible solutions depending on what you actually need it for.

Related

How to receive types built from functors as argument to functions in OCaml?

I'm studying functors, and I was trying to receive a Module.t as argument where Module is the result of a functor call SomeFunctor(sig type t = int end) but I receive Unbound type constructor FooInt.t
Here is the code
module type Foo_S = sig
type t
end
module type Foo = sig
type t
val id : t -> t
end
module FooExtend(Arg : Foo_S) : (Foo with type t := Arg.t) = struct
let id a = a
end
module FooInt = FooExtend(Int)
module FooString = FooExtend(String)
let () =
assert (FooInt.id 1 = 1);
assert (FooString.id "x" = "x")
module FooSimple = struct type t = int end
let foo (x : FooInt.t) = (* This doens't compile: Unbound type constructor FooInt.t *)
FooInt.id x
let foo' (x : FooSimple.t) = (* This works fine *)
FooInt.id x
Two changes are needed, 1) type t needs to be defined in the module, and 2) don't use destructive substitution:
module FooExtend(Arg : Foo_S) : (Foo with type t = Arg.t) = struct
type t = Arg.t
let id a = a
end
The problem is that destructive substitution (type t := Arg.t) replaces every occurrence of t with Arg.t. Instead you want to specify type equality, using a "sharing constraint" (type t = Arg.t).
Notice the difference in the resulting module signature between the two:
module FooExtend(Arg : Foo_S) : (Foo with type t := Arg.t) = struct
type t = Arg.t
let id a = a
end
module FooExtend : functor (Arg : Foo_S) -> sig
val id : Arg.t -> Arg.t
end
module FooExtend(Arg : Foo_S) : (Foo with type t = Arg.t) = struct
type t = Arg.t
let id a = a
end
module FooExtend : functor (Arg : Foo_S) -> sig
type t = Arg.t
val id : t -> t
end

OCaml serializing a (no args) variant as a "string enum" (via Yojson)

Say I am building a record type:
type thing {
fruit: string;
}
But I want the possible values of fruit to be constrained to a fixed set of strings.
It seems natural to model this in OCaml as a variant, e.g.:
type fruit = APPLE | BANANA | CHERRY
type thing {
fruit: fruit;
}
Okay so far.
But if I use [##deriving yojson] on these types then the serialized output will be like:
{ "fruit": ["APPLE"] }
By default Yojson wants to serialize a variant as a tuple of [<name>, <args>...] which... I can see the logic of it, but it is not helpful here.
I want it to serialize as:
{ "fruit": "APPLE" }
Making use of a couple of ppx deriving plugins I managed to build this module to de/serialize as I want:
module Fruit = struct
type t = APPLE | BANANA | CHERRY [##deriving enum, variants]
let names =
let pairs i (name, _) = (name, (Option.get (of_enum i))) in
let valist = List.mapi pairs Variants.descriptions in
List.to_seq valist |> Hashtbl.of_seq
let to_yojson v = `String (Variants.to_name v)
let of_yojson = function
| `String s -> Hashtbl.find_opt names s
|> Option.to_result ~none:(Printf.sprintf "Invalid value: %s" s)
| yj -> Error (Printf.sprintf "Invalid value: %s" (Yojson.Safe.to_string yj))
end
Which works fine... but I have some other "string enum" variants I want to treat the same way. I don't want to copy and paste this code every time.
I got as far as this:
module StrEnum (
V : sig
type t
val of_enum : int -> t option
module Variants : sig
val descriptions : (string * int) list
val to_name : t -> string
end
end
) = struct
type t = V.t
let names =
let pairs i (name, _) = (name, (Option.get (V.of_enum i))) in
let valist = List.mapi pairs V.Variants.descriptions in
List.to_seq valist |> Hashtbl.of_seq
let to_yojson v = `String (V.Variants.to_name v)
let of_yojson = function
| `String s -> Hashtbl.find_opt names s
|> Option.to_result ~none:(Printf.sprintf "Invalid StrEnum value: %s" s)
| yj -> Error (Printf.sprintf "Invalid StrEnum value: %s" (Yojson.Safe.to_string yj))
end
module Fruit = struct
type t = APPLE | BANANA | CHERRY [##deriving enum, variants]
end
module FruitEnum = StrEnum (Fruit)
That much seems to type-check, and I can:
utop # Yojson.Safe.to_string (FruitEnum.to_yojson Fruit.APPLE);;
- : string = "\"APPLE\""
utop # FruitEnum.of_yojson (Yojson.Safe.from_string "\"BANANA\"");;
- : (FruitEnum.t, string) result = Ok Fruit.BANANA
...but when I try to:
type thing {
fruit: FruitEnum.t;
}
[##deriving yojson]
I get Error: Unbound value FruitEnum.t
It seems to be because I am re-exporting type t = V.t from the variant's module, I don't really understand though. (Or is it because the yojson ppx can't "see" the result of the functor properly?)
How can I fix this?
I would also like to be able to skip defining the variant module separately and just do:
module Fruit = StrEnum (struct
type t = APPLE | BANANA | CHERRY [##deriving enum, variants]
end)
...but this gives the error:
Error: This functor has type
functor
(V : sig
type t
val of_enum : int -> t option
module Variants :
sig
val descriptions : (string * int) list
val to_name : t -> string
end
end)
->
sig
type t = V.t
val names : (string, t) Hashtbl.t
val to_yojson : t -> [> `String of string ]
val of_yojson : Yojson.Safe.t -> (t, string) result
end
The parameter cannot be eliminated in the result type.
Please bind the argument to a module identifier.
and I don't understand what is wrong.
Regarding the last error, it's because OCaml requires a 'stable path' to types inside modules so it can refer to them. A stable path is a named path to a type, e.g. Fruit.t.
By contrast, StrEnum(struct type t = ... end).t is not a stable path because the type t is referencing a type t in the module literal which does not have a name.
Long story short, you basically can't skip defining the variant module separately. But it's simple to do it in two steps:
module Fruit = struct
type t = ...
end
module Fruit = StrEnum(Fruit)
The second definition refers to the first and shadows it. Shadowing is a well-known and often-used technique in OCaml.
Overall, I'm not sure all this PPX machinery is actually justified. You can pretty easily hand-write converter functions, e.g.
let to_yojson = function
| APPLE -> `String "APPLE"
| BANANA -> `String "BANANA"
| CHERRY -> `String "CHERRY"
Well, I was curious to have a go at writing a PPX deriver to perform this transformation.
Here's what I ended up with:
open Ppxlib
module List = ListLabels
let make_methods ~(loc : location) ~(is_poly : bool) (constructors : constructor_declaration list) =
let (module Ast) = Ast_builder.make loc in
let v_patt = match is_poly with
| true -> fun name -> Ast.ppat_variant name None
| false -> fun name -> Ast.ppat_construct { txt = (Lident name); loc } None
and v_expr = match is_poly with
| true -> fun name -> Ast.pexp_variant name None
| false -> fun name -> Ast.pexp_construct { txt = (Lident name); loc } None
in
let (to_cases, of_cases) =
List.map constructors ~f:(
fun cd ->
let name = cd.pcd_name.txt in
let to_case = {
pc_lhs = v_patt name;
pc_guard = None;
pc_rhs = [%expr `String [%e Ast.estring name] ];
} in
let of_case = {
pc_lhs = Ast.ppat_variant "String" (Some (Ast.pstring name));
pc_guard = None;
pc_rhs = [%expr Ok ([%e v_expr name]) ];
} in
(to_case, of_case)
)
|> List.split
in
let of_default_case = {
pc_lhs = [%pat? yj ];
pc_guard = None;
pc_rhs = [%expr Error (Printf.sprintf "Invalid value: %s" (Yojson.Safe.to_string yj)) ];
} in
let of_cases = of_cases # [of_default_case] in
let to_yojson = [%stri let to_yojson = [%e Ast.pexp_function to_cases]] in
let of_yojson = [%stri let of_yojson = [%e Ast.pexp_function of_cases] ] in
[to_yojson; of_yojson]
let type_impl ~(loc : location) (td : type_declaration) =
match td with
| {ptype_kind = (Ptype_abstract | Ptype_record _ | Ptype_open); _} ->
Location.raise_errorf ~loc "Cannot derive yojson_str_enum for non variant types"
| {ptype_kind = Ptype_variant constructors; _} -> begin
let invalid_constructors =
List.filter_map constructors ~f:(
fun cd -> match cd.pcd_args with
| (Pcstr_tuple [] | Pcstr_record []) -> None
| _ -> Some (cd)
)
in
if (List.length invalid_constructors) > 0 then
Location.raise_errorf ~loc "Cannot derive yojson_str_enum for variant types with constructor args";
match is_polymorphic_variant td ~sig_:false with
| `Definitely | `Maybe -> make_methods ~loc ~is_poly:true constructors
| `Surely_not -> make_methods ~loc ~is_poly:false constructors
end
let generate_impl ~ctxt (_rec_flag, type_declarations) =
(* [loc] is "location", not "lines of code" *)
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
List.map type_declarations ~f:(type_impl ~loc)
|> List.concat
let yojson_str_enum =
Deriving.add
"yojson_str_enum"
~str_type_decl:(Deriving.Generator.V2.make_noarg generate_impl)
to make usable it needs a dune file something like:
(library
(kind ppx_rewriter)
(name <lib name>)
(preprocess (pps ppxlib.metaquot))
(libraries yojson ppxlib))
After adding <lib name> to the pps in your dune file, usage is like:
module Fruit = struct
type t = APPLE | BANANA | CHERRY [##deriving yojson_str_enum]
end
It seems to work fine for my use case. It might be extended per the comment by #Yawar to take args allowing to specify to/from string transform functions for the variant labels. But I was happy just with Fruit.APPLE -> "APPLE" for now. I should also implement the sig_type_decl version.
One part I am a bit uncertain about is this:
match is_polymorphic_variant td ~sig_:false with
| `Definitely | `Maybe -> make_methods ~loc ~is_poly:true constructors
| `Surely_not -> make_methods ~loc ~is_poly:false constructors
I am not very clear when the `Maybe case occurs or how it should most correctly be handled, or if there is a better way of detecting "backtick variants" than using the is_polymorphic_variant method from ppxlib.

Function returning module created by functor

I have problems defining the type of a module that is returned by a functor. Could anyone solve this?
module type ANIMALTYPE = sig
val age : unit -> int
end
module type SHIPGENERATOR = sig
val age : unit -> int
(* Another possibility: include ANIMALTYPE *)
val hello : string
end
module RabbitModule : ANIMALTYPE = struct
let age () = 10
end
module Make_ShipGenerator (A : ANIMALTYPE) = struct
let age = A.age
let hello = "world"
end
let get_shipgenerator race = match race with
| Rabbit -> let module M = (Make_ShipGenerator (RabbitModule))
in (module M : SHIPGENERATOR)
Edit: Added hello to Make_ShipGenerator.
Edit 2: Added module type SHIPGENERATOR, which will include those parts needed from ANIMALTYPE.
Well, it is ANIMALTYPE, what else?
let module M = (Make_ShipGenerator (RabbitModule))
in (module M : ANIMALTYPE);;

How to write a functor over arrays and strings?

I would like to make my code generic over strings and arrays (any indexable type really) using the following signature:
module type Indexable = sig
type 'a t
val get : int -> 'a t -> 'a
end
module MyCode (I : Indexable) = struct ... end
But of course I cannot apply my signature to strings as follows:
module StrMyCode = MyCode(struct
type 'a t = string
let get i a = a.[i]
end)
Is there any way to fix this issue? Or perhaps a different aprroach? I know I can use arrays of characters in the worst case but I'd rather save my code from ugly casts and this is something that was on my mind before so I'd like to get a clear answer for this.
GADT can be used with the functorized approach:
module type Indexable = sig
type 'a t
val get: int -> 'a t -> 'a
end
module MyCode(I:Indexable) = struct
let head x = I.get 0 x
end
Arrays can of course be made Indexable trivially:
module IndexableArray = struct
type 'a t = 'a array
let get i x = x.(i)
end
For string, you can just use a GADT with a single constructor. Note however, that you have to put some type annotation for get in order to force the polymorphic type (otherwise, the inferred type is int -> char t -> char):
module IndexableString = struct
type 'a t = String: string -> char t
let of_string s = String s
let get: type a. int -> a t -> a =
fun i s -> match s with String s -> s.[i]
end
Here is something I made using GADTs. I'm just wrapping my head around them, so there may be something a little wrong here. But it seems to work as far as I can see (with OCaml 4):
type _ indexable =
| A : 'a array -> 'a indexable
| C : string -> char indexable
let index (type s) (x: s indexable) i : s =
match x with
| A a -> a.(i)
| C s -> s.[i]
let main () =
let x = A [| 1; 2 |] in
let y = C "abc" in
Printf.printf "%d\n" (index x 0);
Printf.printf "%c\n" (index y 1)
If I load into the toplevel, I get this:
val index : 'a indexable -> int -> 'a = <fun>
val main : unit -> unit = <fun>
# main ();;
1
b
- : unit = ()
#
This might not be as general as what you're looking for.
If you declare the element type of the indexable as a separate type, you can do something like this:
module type Indexable = sig
type t
type elt
val get : int -> t -> elt
end
module IndexableString : Indexable = struct
type t = string
type elt = char
let get i a = a.[i]
end
module MyCode (I : Indexable) = struct
(* My code implementation *)
end
module StrMyCode = MyCode(IndexableString)
For arrays, you can do more or less the same:
module ArrayIndexable = struct
type elt = char
type t = char array
let get i a = a.(i)
end
Now, if you wish to retain some flexibility with arrays, you may change the above into a functor:
module ArrayIndexable (E : sig type e end) : Indexable with type elt = E.e =
struct
type elt = e
type t = elt array
let get i a = a.(i)
end
It is more verbose than the polymorphic version you are looking for, but it let you encode both "indexable" types uniformly.

Convert 'a list to a Set?

Is it really true that OCaml doesn't have a function which converts from a list to a set?
If that is the case, is it possible to make a generic function list_to_set? I've tried to make a polymorphic set without luck.
Fundamental problem: Lists can contain elements of any types. Sets (assuming you mean the Set module of the standard library), in contrary, rely on a element comparison operation to remain balanced trees. You cannot hope to convert a t list to a set if you don't have a comparison operation on t.
Practical problem: the Set module of the standard library is functorized: it takes as input a module representing your element type and its comparison operation, and produces as output a module representing the set. Making this work with the simple parametric polymoprhism of lists is a bit sport.
To do this, the easiest way is to wrap your set_of_list function in a functor, so that it is itself parametrized by a comparison function.
module SetOfList (E : Set.OrderedType) = struct
module S = Set.Make(E)
let set_of_list li =
List.fold_left (fun set elem -> S.add elem set) S.empty li
end
You can then use for example with the String module, which provides a suitable compare function.
module SoL = SetOfList(String);;
SoL.S.cardinal (SoL.set_of_list ["foo"; "bar"; "baz"]);; (* returns 3 *)
It is also possible to use different implementation of sets which are non-functorized, such as Batteries and Extlib 'PSet' implementation (documentation). The functorized design is advised because it has better typing guarantees -- you can't mix sets of the same element type using different comparison operations.
NB: of course, if you already have a given set module, instantiated form the Set.Make functor, you don't need all this; but you conversion function won't be polymorphic. For example assume I have the StringSet module defined in my code:
module StringSet = Set.Make(String)
Then I can write stringset_of_list easily, using StringSet.add and StringSet.empty:
let stringset_of_list li =
List.fold_left (fun set elem -> StringSet.add elem set) StringSet.empty li
In case you're not familiar with folds, here is a direct, non tail-recursive recursive version:
let rec stringset_of_list = function
| [] -> StringSet.empty
| hd::tl -> StringSet.add hd (stringset_of_list tl)
Ocaml 3.12 has extensions (7,13 Explicit naming of type variables and 7,14 First-class modules) that make it possible to instantiate and pass around modules for polymorphic values.
In this example, the make_set function returns a Set module for a given comparison function and the build_demo function constructs a set given a module and a list of values:
let make_set (type a) compare =
let module Ord = struct
type t = a
let compare = compare
end
in (module Set.Make (Ord) : Set.S with type elt = a)
let build_demo (type a) set_module xs =
let module S = (val set_module : Set.S with type elt = a) in
let set = List.fold_right S.add xs S.empty in
Printf.printf "%b\n" (S.cardinal set = List.length xs)
let demo (type a) xs = build_demo (make_set compare) xs
let _ = begin demo ['a', 'b', 'c']; demo [1, 2, 3]; end
This doesn't fully solve the problem, though, because the compiler doesn't allow the return value to have a type that depends on the module argument:
let list_to_set (type a) set_module xs =
let module S = (val set_module : Set.S with type elt = a) in
List.fold_right S.add xs S.empty
Error: This `let module' expression has type S.t
In this type, the locally bound module name S escapes its scope
A possible work-around is to return a collection of functions that operate on the hidden set value:
let list_to_add_mem_set (type a) set_module xs =
let module S = (val set_module : Set.S with type elt = a) in
let set = ref (List.fold_right S.add xs S.empty) in
let add x = set := S.add x !set in
let mem x = S.mem x !set in
(add, mem)
If you don't mind a very crude approach, you can use the polymorphic hash table interface. A hash table with an element type of unit is just a set.
# let set_of_list l =
let res = Hashtbl.create (List.length l)
in let () = List.iter (fun x -> Hashtbl.add res x ()) l
in res;;
val set_of_list : 'a list -> ('a, unit) Hashtbl.t = <fun>
# let a = set_of_list [3;5;7];;
val a : (int, unit) Hashtbl.t = <abstr>
# let b = set_of_list ["yes";"no"];;
val b : (string, unit) Hashtbl.t = <abstr>
# Hashtbl.mem a 5;;
- : bool = true
# Hashtbl.mem a 6;;
- : bool = false
# Hashtbl.mem b "no";;
- : bool = true
If you just need to test membership, this might be good enough. If you wanted other set operations (like union and intersection) this isn't a very nice solution. And it's definitely not very elegant from a typing standpoint.
Just extend the original type, as shown in
http://www.ffconsultancy.com/ocaml/benefits/modules.html
for the List module:
module StringSet = Set.Make (* define basic type *)
(struct
type t = string
let compare = Pervasives.compare
end)
module StringSet = struct (* extend type with more operations *)
include StringSet
let of_list l =
List.fold_left
(fun s e -> StringSet.add e s)
StringSet.empty l
end;;
Using the core library you could do something like:
let list_to_set l =
List.fold l ~init:(Set.empty ~comparator:Comparator.Poly.comparator)
~f:Set.add |> Set.to_list
So for example:
list_to_set [4;6;3;6;3;4;3;8;2]
-> [2; 3; 4; 6; 8]
Or:
list_to_set ["d";"g";"d";"a"]
-> ["a"; "d"; "g"]