Is it possible in Standard ML to re-export the constructors of a datatype that's part of a structure received as a functor argument. Some code will probably make this easier to understand:
signature FLAG =
sig
type t
end
signature MEMBER =
sig
structure Flag : FLAG
end
functor Member(F : FLAG) : MEMBER =
struct
structure Flag = F
end
structure M =
Member(struct
datatype t =
FLAG_1
| FLAG_2
end)
val flag1 = M.Flag.FLAG_1;
(* Error: unbound variable or constructor: FLAG_1 in path M.Flag.FLAG_1 *)
The above example may not make any sort of practical sense, but it's just a watered down version of an issue that I encountered in one of my projects.
If I understand the situation correctly, the unelaborated type specification in the FLAG
signature entails that t
is left opaque, and thus inaccessible for anything outside of the structures that implement FLAG
.
Generally speaking, in SML, if a signature specifies an interface for a module, then the only parts of that module that can be accessed from without are those which are explicitly described in the signature. As you likely know, if you specify an interface for some module, then only those functions and values you explicitly declare in the signature are provided for use; all those which are omitted become sealed inside the module. The same principle is at work here with the unexplained specification of type t
: since the signature does not give any account of how this type is constituted, no information about it is available.
So you can easily re-export the value constructors from a module given as a parameter to a functor, provided you have included those constructors in the specifications of that module's interface. E.g.,
signature FLAG =
sig
datatype t = FLAG_1 | FLAG_2
end
signature MEMBER =
sig
structure Flag : FLAG
end
functor Member(F : FLAG) : MEMBER =
struct
structure Flag = F
end
structure M =
Member(struct
datatype t =
FLAG_1
| FLAG_2
end)
And then
- val a = M.Flag.FLAG_1;
val a = FLAG_1 : ?.t
The most important point to take note of here is probably this: the inaccessibility of the value constructors in the module implementing FLAG
have everything to do with the way the interface is specified and nothing to do with the fact that it appears here as a parameter to functor Member
. We get the same behavior you observed when you were using a functor with the following program:
signature FLAG =
sig
type t
end
structure F : FLAG =
struct
datatype t =
FLAG_1
| FLAG_2
end
And then
[opening ~/Programming/sml/scratch/scratch.sml]
signature FLAG = sig type t end
structure F : FLAG
val it = () : unit
- F.FLAG_1;
stdIn:63.1-63.9 Error: unbound variable or constructor: FLAG_1 in path F.FLAG_1