Functors in OCaml: triple code duplication necessary? - ocaml

I'd like to clarify one point: currently it seems to me that triple signature duplication is necessary while declaring a functor, provided we export it in the .mli file. Here is an example:
Suppose we have a functor Make, which produces a module A parametrized by SigA (simplest example I could think of). Consequently, in the .mli file we have:
module type A = sig
type a
val identity : a -> a
end
module type SigA = sig
type a
end
module Make (MA:SigA) :
A with type a := MA.a
Now I understand that we have to write an implementation in the .ml file:
module Make (MA:SigA) = struct
type a = MA.a
let identity obj = obj
end
So far so good, right? No! Turns out we have to copy the declaration of A and SigA verbatim into the .ml file:
module type A = sig
type a
val identity : a -> a
end
module type SigA = sig
type a
end
module Make (MA:SigA) = struct
type a = MA.a
let identity obj = obj
end
While I (vaguely) understand the rationale behind copying SigA (after all, it is mentioned in the source code), copying A definition seems like a completely pointless exercise to me.
I've had a brief look through the Core codebase, and they just seem to either duplicate it for small modules and for larger once they export it to the separate .mli, which is used both from .ml and .mli.
So is it just a state of affairs? Is everyone fine with copying the module signature THREE times (once in the .mli file, two times in the .ml file: declaration and the definition!!)
Currently I'm considering just ditching .mli files altogether and restricting the modules export using signatures in the .ml files.
EDIT: yes I know that I can avoid this problem by declaring the interface for A inline inside Make in the .mli file. However this doesn't help me if I want to use that interface from outside of that module.

That's because a pair of ML and MLI file acts like a structure and a corresponding signature it is matched against.
The usual way to avoid writing out the module type twice is to define it in a separate ML file. For example,
(* sig.ml *)
module type A = sig
type a
end
module type B = sig
type b
val identity : b -> b
end
(* make.mli *)
module Make (A : Sig.A) : Sig.B with type b = A.a
(* make.ml *)
module Make (A : Sig.A) =
struct
type b = A.a
let identity x = x
end
It is fine to leave out an MLI file in the case where it does not hide anything, like for the Sig module above.
In other cases, writing out the signature separately from the implementation is a feature, and not really duplication -- it defines the export of a module, and usually, that is a small subset of what's in the implementation.

Related

What does val mean in Ocaml

When seeing documentation of modules in Ocamel there is something like this for example in Graphics Module
val close_graph : unit -> unit
Or when writing a function in an interactive mode:
# let x () = 3;;
val x : unit -> int = <fun>
there is val x : unit -> int = <fun>, What is val and it's use case?
Well, val is a keyword in OCaml with several different uses.
The cases you mention are both, in essence, that val is used in a module signature to specify values that appear in the module. Values are things like functions and expressions. (An example of something that's not a value that can appear in a module is a type.)
You can read about module signatures in the OCaml manual.
The first variant of the nonterminal specification is the one that begins with val.
(In the toplevel, you are creating a module as you type in your definitions. So the toplevel is using signature-style syntax to show what you've defined. So it seems to me anyway.)

OCAML module contains type variables that cannot be generalized

Code:
let size = 10
let getTbl = Array.init size ~f:(fun _ -> Avltree.empty )
end
Error:
Error: The type of this module,
sig val size : int val getTbl : ('_weak1, '_weak2) Avltree.t array end,
contains type variables that cannot be generalized
How do I let the Ocaml compiler know that I plan to store both my key's and values as ints?
Have tried a few different approaches - none of which have worked.
Weak type variables denote types that are not yet inferred, usually because you have defined a program variable and never used it, so the type checker has no idea what this variable contains. It is fine, in general, as the first usage of the variable will define its type. However, since the whole type checking routine in OCaml is bounded by the scope of a compilation unit (i.e., a file), such variables should be defined before you compile your file.
Therefore, you have to either (1) use the variable, (2) constraint it to some type, e.g., (let getTbl : (int, int) Avltree.t array) .. in the implementation (.ml) file, or (3) in the mli file. You can even just create an empty .mli file (with the same name as you .ml file) and this will automatically hide all variables defined in your module and enable compilation.
It might work to change Avltree.empty to (Avltree.empty : (int, int) Avltree.t)

Module dependency cycle

I have:
Module 1:
provides type Module1.type1, its constructor, and some functions that accept and return type1
Module 2:
open Module1
open Module3
provides type Module2.type2, also has functions that accept type1 and type3 as params
Module 3:
open Module1
open Module2
provides type Module3.type3, and its constructor that depends on type1
provides functions that accept and return types type1, type2 and type3
question
as a result I obviously get dependency cycle: src/Module3.cmj -> src/Module2.cmj -> src/Module3.cmj error by compiler. Something that is trivially achievable in TypeScript/JS with individual import, is not possible in Reason. How to get around this?
I don't really want to change the architecture of my program, just to facilitate shortcomings of compiler/module system.
The simplest way to handle your problem is indeed recursive modules. I don't advise you to use them, as recursive modules can make your code harder to read, compile and can in the most complex cases break your code at run time. Not to mention if you use side-effects in your module definitions (please don't).
I will use the OCaml syntax, you should be able to easily translate to Reason.
If you want to go with that anyway, here is the quick and dirty solution, using recursive module and functors.
The quick and dirty solution
1) Create a module myModTypes that will indicate the expected types of module2 and module3. It should look like:
module type Module2type = sig ... end
module type Module3type = sig ... end
with ... being the expected signatures of your modules (if you already have interface files written, just copy/paste them here, if you don't write those, they are important)
2) Put module2 and module3 within functors expecting the other module
For example, the code of module2 should now look like
module MakeModule2(Module3 : MyModTypes.Module3type) = struct
(* the code of module2 *)
end
The code of module3 will be in the same way, just swap 2 and 3 in the added lines.
3) Create a module makemodules2and3 with that code (translated to Reason):
module rec Module2 : MyModTypes.Module2type = Module2.MakeModule2(Module3)
and Module3 : MyModTypes.Module3type = Module3.MakeModule3(Module2)
Note that recursive module definitions always expect a module type.
4) Subsequent uses of Module2 and Module3 should now open Makemodules2and3 before being able to use them.
The right solution
You have to change the architecture of your program. Slightly.
As the OP said, there are no cycle of dependency in the functions, and that's a relief. Just split module2 and module3 into two new modules each. One with the functions that only depend on module1 and their own module, one with the "next step" functions.
This is a better way to approach how you declare your modules: they should be one with the types they define. Ideally, you have a module for each type, plus one additional module for each interaction between types.
Looks like Module1 doesn't depend on the other two modules. You can keep it as is. But since the other two are mutually recursive, you can express that using the recursive module syntax. This does have a requirement though that you declare the module signatures at the point of definition, since Reason needs to know what to expect. For example:
/* Impl.re */
module rec Module2: {
type type2;
let make: (Module1.type1, Module3.type3) => type2;
} = {
... actual implementation of Module2 ...
} and Module3: {
type type3;
let make: Module1.type1 => type3;
let foo: Module1.type1;
let bar: Module2.type2 => type3;
} = {
... actual implementation of Module3 ...
};
This is the general shape you'd use, you can adapt it to your needs.
If you don't want your users to have to do Impl.Module2.... to access the recursive modules, you can even expose them as file modules using include:
/* Module2.re */
include Impl.Module2;
And you can even annotate the implementation modules (Impl.Module2 and 3) with a compile-time warning to let users know not to use those ones:
/* Impl.re */
[#deprecated "Please use Module2 instead of Impl.Module2"]
module Module2: {...} = {...};

Resolve library conflict in SML/NJ Compilation Manager

I'm using SML/NJ 110.79, which includes support for new structures defined by the Successor ML project. Among others, the Fn structure.
As it happens, I already had an identically named structure in one of my personal project with utilities, which worked fine before 110.79.
With 110.79, for this .cm file:
group is
$/basis.cm
$SMACKAGE/sml-extras/v0.1.0/sources.sml.cm
I get the following error, though:
sources.cm:3.3-3.45 Error: structure Fn imported from
$SMLNJ-BASIS/(basis.cm):basis-common.cm#155252(fn.sml) and also from
$SMACKAGE/sml-extras/v0.1.0/(sources.sml.cm):src/fn.sml
Does anyone know how to resolve this conflict through the Compilation Manager. Ideally, my Fn structure will be able to "extend" the standard Fn by just open-ing it, but projects using the sml-extras library, will not see the standard Fn structure, only my extended version.
Is this possible? Do I need to wrap/re-export the whole basis.cm library in my sml-extras.cm project?
I managed to solve this by using what I believe is called an administrative library in the CM manual, §2.9.
What that means precisely is to create an auxiliary .cm file that wraps the basis library and re-exports only the symbols we're interested in.
sources.cm
This is the main project file.
library
structure Main
is
(* Let's say this library redefines the Fn and Ref structures *)
(* and the REF signature. *)
$SMACKAGE/sml-extras/v0.1.0/sources.sml.cm
(* This excludes out Fn, Ref and REF from the Basis library, but *)
(* imports anything else. *)
amended-basis.cm
main.sml
amended-basis.cm
This file imports $/basis.cm and then re-exports all of it except Fn, Ref and REF.
group
library($/basis.cm) - (
structure Fn
structure Ref
signature REF
)
is
$/basis.cm
main.sml
structure Main =
struct
open Fn (* sml-extras's Fn *)
end
The solution is based on the set calculus described in the CM manual, §4 and on the EBNF grammar from Appendix A.
Another solution would have been to change sml-extras to re-export the whole $/basis.cm, while shadowing the conflicting symbols. However, in the interest of modularity I decided to go with the solution detailed above.

Using external type declarations with OCamlyacc

I have a type expr in an expr.ml file. In parser.mly (OCamlyacc file), I define the expr rule and give the type :
%start expr
%type <expr> expr
However, I get :
File "parser.mli", line 34, characters 48-52:
Error: Unbound type constructor expr
I tried adding
%{
open Expr
%}
at the beginning of the .mly file but it still doesn't work. How may I define this expr type in an external file and use it as the return value of my rule? Thanks.
You need to qualify expr type with the module name. I.e., if it is defined in expression.ml (using type expr = ...) you should use
%type <Expresssion.expr> main
Note the capital E when using the module name.
I"m not sure if I'm understanding correctly.
But you are struggling with a circular dependency? Let's say T contains your type and calls the parser, P. P cannot produce type T.t since T depends on P, not the other way around. Normally, I've created a third file that contains the type information, T'.
For example,
T.ml
let parse filename : T'.t =
filename
|> open_in
|> Lexing.from_channel
|> P.command L.token
P.mly
%type <T'.t> command
%start command
%%
T'.ml
type t = Label of String
| Integer of String
| Float of string
| Star of t
Ocamlyacc doesn't let you specify text to be generated in the interface (.mli) file. So wherever you specify a type that goes into the interface (the type of a token or rule), you need to use a fully-qualified type.
Here it looks like you can use a fully-qualified type, but sometimes that's not possible because the type involves a functor application. There are several workarounds:
Arrange to build all functors in a separate compilation unit. This is easy, but doesn't work e.g. if the functors involve the token type.
Do post-processing on the ocamlyacc-generated .mli file to add a header. You can do pretty much anything this way, but it's ugly and annoying.
Use Menhir, an improved replacement of Ocamlyacc. It's an additional dependency, but it does solve Ocamlyacc's main shortcomings.