Structure the code in modules

Hello,

I have troubles in splitting my app into modules & submodules using dune.

Here is my current project tree (following this SO answer: ocaml - Running OUnit tests using dune - Stack Overflow ) :

├── dune
├── dune-project
├── booleanFormulae
│   ├── booleanFormulae.ml // begins with "open Types"
│   ├── booleanFormulae.mli // begins with "open Types"
│   ├── dune
│   ├── parser.ml // begins with "open Types"
│   └── types.ml
├── main.ml
├── test
│   ├── dune
│   └── test.ml

Here is the root dune file:

(executable
 (name main)
 (libraries BooleanFormulae))

Here is lib/dune:

(library 
    (name BooleanFormulae)
    (libraries angstrom)
    (preprocess (pps ppx_deriving.show ppx_deriving.ord)))

In the dune docs (Stanza Reference — Dune documentation), it is said

By default dune will use all the .ml/.re files in the same directory as the dune file.

So if I understand well, in the main.ml I should be able to open BooleanFormulae.Parser or
open BooleanFormulae.Types.
However, dune build main.exe complains:

File "main.ml", line 2, characters 5-27:
2 | open BooleanFormulae.Parser
         ^^^^^^^^^^^^^^^^^^^^^^
Error: Unbound module BooleanFormulae.Parser

Just referring to Parser in main.ml doesn’t work either.

What should I do? And is my project globally well structured?

Related I think:

in the test/test.ml, I cannot refer to the values in types.ml (included in the booleanFormulae.mli interface) [Unbound constructor …], but I can do it in the main.ml file.

Here is my test/dune:

(test (name test) (libraries BooleanFormulae ounit2))

I might be wrong, but the message is
“unbound module BooleanFormulae.Parser”, not “unbound module BooleanFormulae”
Can we see your booleanFormular.mli ?

But also, your library is called BooleanFormulae and your file is BooleanFormulae
I’ve had issues with first letter case sensitivity in the past, try to have them matching

Hi, thank you for trying to help. Change the case of my file to uppercase doesnt seem to get things better.

Here is my BooleanFormulae.mli:

open Types

type decTree = DecLeaf of bool | DecRoot of string * decTree * decTree
module VarsSet :
sig
(* set signature *)
end

val getVars : tformula -> VarsSet.t
type env 
val empty_env : env
val env_of_bindings : (string * bool) list -> env
val evalFormula : env -> tformula -> bool
val buildDecTree : tformula -> decTree

type bddGraph

val buildBdd : tformula -> bddGraph
val isTautology : tformula -> bool
val areEquivalent : tformula -> tformula -> bool
module Dot :
sig

  type style = Red | Dashed | Bold
  type elt = Node of (int * string) | Vertex of (int * int)

  type digraph

  val empty : digraph
  val join : digraph -> digraph -> digraph
  val addWithStyle : elt -> style list -> digraph -> digraph
  val add : elt -> digraph -> digraph
  val join_in_bracket : string list -> string
  val show : string -> digraph -> string
end
val bddToDot : bddGraph -> Dot.digraph
val decTreeToDot : int -> decTree -> int * Dot.digraph

Ah ! I understand what’s happening now
Didn’t read your tree properly

When you have a file in your directory that has the same name as your library (booleanFormulae here), dune creates a library from this file.
Meaning, in booleanFormulae.ml you need:

module Parser = Parser

and in your booleanFormulae.mli you need

module Parser : sig
...
end
2 Likes

Thank you, problem solved for the BooleanFormulae.Parser. I still have an issue with the tests:

$ dune runtest
File "test/test.ml", line 4, characters 9-12:
4 | let p1 = Var "P1"
             ^^^
Error: Unbound constructor Var

(Var is one of the constructors of tformula which is defined in booleanFormulae/types.ml. It is however accessible in main.ml)

Does it work if you prefix the constructor with its module name: BooleanFormulae.Types.Var?

Careful, open != include, in your mli you open Types, but you don’t include it, so it will not be exported
Unless you fixed it afterwards ?

Thank you @yawaramin, but it doesn’t fix the issue…

@giltho I’ve tried to include Types instead of open Types in booleanFormulae.mli, but I have an “unbounded module type Types”:

File "BooleanFormulae/booleanFormulae.mli", line 2, characters 8-13:
2 | include Types
            ^^^^^
Error: Unbound module type Types

include Types doesn’t work but open Types does ? that doesn’t sound right

I think it would be helpful if you could upload a small sample project with this issue. We could examine it and figure out what should be changed to make it work.

Yes, I think it will be simpler.

Here is the project https://github.com/sebsheep/decision_tree

I don’t know how yet to specify what package install with opam (like the “requirements.txt” in python or “package.json” with npm). The dependencies are: ounit2 and angstrom.

thank you for your time!

That’s in a .mli file. To include the contents of another module, the corresponding signature item is include module type of Types. To expand a bit on this:

  • Types is a module.
  • its (inferred) module type is module type of Types
  • in mli files, include expects a module type (which does not necessarily correspond to a module in particular, which is why you can do include Set.OrderedType)
1 Like

Ah it’s in the mli didn’t catch that indeed !

1 Like

Additionally, you can change

(library 
    (name BooleanFormulae)
    (libraries angstrom)
    (preprocess (pps ppx_deriving.show ppx_deriving.ord)))

to

(library 
    (name BooleanFormulae)
    (libraries angstrom)
    (modules parser types)
    (preprocess (pps ppx_deriving.show ppx_deriving.ord)))

to be able to access BooleanFormulae.Parser from main.ml.

1 Like