5
votes

I have:

module Functor(M : sig end) = struct
  module NestedFunctor(M : sig end) = struct
  end 
end

This code is valid:

module V = Functor(struct end)
module W = V.NestedFunctor(struct end)

But this is invalid:

module M = Functor(struct end).NestedFunctor(struct end)
                          (*  ^ Error: Syntax error *)

As I understand, a functor is a relation between a set of input modules and a set of permissible output modules. But this example confuses my understanding. Why functor result bind to new module name is necessary for call nested functor of?

My compiler version = 4.01.0


I'm new in OCaml. When I found functors I imagined something as

Engine.MakeRunnerFor(ObservationStation
                       .Observe(Matrix)
                       .With(Printer))

I thought it is a good tool for the human friendly architecture notation. Then I was disappointed. I understand: this is syntax error. And I think this restrictions inflate grammar, and makes it less intuitive.

And my "Why?" in main question is in the context of the concept of language.

3

3 Answers

8
votes

While I don't believe that this restriction is strictly necessary, it is probably motivated by certain limitations in OCaml's module type system. Without going into too much technical detail, OCaml requires all intermediate module types to be expressible as syntactic signatures. But with functors, that sometimes isn't possible. For example, consider:

module Functor(X : sig end) = struct
  type t = T of int
  module Nested(Y : sig end) = struct let x = T 5 end 
end

Given this definition, the type of the functor Functor(struct end).Nested can't be expressed in OCaml syntax. It would need to be something like

functor(Y : sig end) -> sig val x : Functor(struct end).t end  (* not legal OCaml! *)

but Functor(struct end).t isn't a valid type expression in OCaml, for reasons that are rather technical (in short, allowing a type like that would make deciding what types are equal -- as necessary during type checking -- much more involved).

Naming intermediate modules often avoids this dilemma. Given

module A = Functor(struct end)

the functor A.Nested has type

functor(Y : sig end) -> sig val x : A.t end

by referring to the named intermediate result A.

1
votes

Using the terminology in the manual, types and the like (module types, class types, etc.) can be qualified by an extended-module-path where a qualifier can be a functor call, whereas non-types (core expressions, module expressions, classes, etc.) can only be qualified by a module-path where qualifiers have to be plain module names.
For example, you can write a type Functor(struct end).NestedFunctor(struct end).t but not an expression Functor(struct end).NestedFunctor(struct end).x or a module expression Functor(struct end).NestedFunctor(struct end).

Syntax-wise, allowing extended-module-path in expressions would be ambiguous: the expression F(M).x is parsed as the constructor F applied to the expression (M).x, where M is a constructor and the . operator is the record field access operator. This won't ever typecheck since M is obviously a variant to which the . operator can't be applied, but eliminating this at the parser would be complicated. There may be other ambiguities I'm not thinking of right now (with first-class modules?).

As far as the type checker is concerned, functor calls in types designation isn't a problem — they're allowed. However the argument has to itself be a path: you can write Set.Make(String).t but not Set.Make(struct type t = string let compare = … end).t. Allowing structures and first-class modules in type expressions would make the type checker more complex, because of the way OCaml manages abstract types. Every time you write Set.Make(String).t, it designates the same abstract type; but if you write

module M1 = Set.Make(struct type t let compare = String.compare end)
module M2 = Set.Make(struct type t let compare = String.compare end)

then M1 and M2 are distinct abstract types. The technical way to formulate this is that in OCaml, functor application is applicative: applying the same functor to the same argument always returns the same abstract type. But structures are generative: writing struct … end twice produces distinct abstract types — so Set.Make(struct type t let compare = String.compare end).tSet.Make(struct type t let compare = String.compare end).t — generative types lead to a non-reflexive equality between type expressions if you aren't careful what you allow in type expressions.

Code generation wouldn't be impacted much, because it could desugar Functor(struct … end).field as let module TMP = struct … end in Functor(TMP).field.

0
votes

As far as I can see, there's no deep answer. The reported error is a syntax error. I.e., the grammar of OCaml just doesn't support this notation.

One way to summarize it is that in the grammar for a module expression, the dot always appears as part of a "long module identifier", i.e., between two capitalized identifiers. I checked this just now, and that's what I saw.