Signature inside of a structure - structure

I want to place signature/structure pair inside a structure, like so:
structure Outer :> OUTER =
struct
signature INNER =
sig
...
end
structure Inner :> INNER =
struct
...
end
end
but even the simplest of examples produces an error:
../test.sml:1.18-2.6 Error: syntax error: replacing STRUCT with EQUALOP
../test.sml:5.6 Error: syntax error found at END
It appears that signatures are not allowed inside structures. What is the best way to achieve this functionality?

Although structures nest in SML, signatures do not. It's not clear what functionality you want to achieve:
Hiding a named signature is impossible.
Having the INNER signature depend on types declared in structure Outer is achieved through fibration (the where type clause). There's a length section on fibration in the tutorial by Harper and Pierce in Benjamin Pierce's book on advanced types in programming languages.

You could also inline the inner signature, as in
structure Outer :> OUTER =
struct
structure Inner :> sig
...
end
=
struct
...
end
end

Related

In OCaml, what does aliasing a module do exactly?

In OCaml, to bring another module in scope you can use open. But what about code like this:
module A = struct
include B.C
module D = B.E
end
Does this create an entirely new module called A that has nothing to do with the modules created by B? Or are the types in B equivalent to this new structure and can a type in A.t can be used interchangeably with a type in B.C.t for example?
Especially, comparing to Rust I believe this is very different from writing something like
pub mod a {
pub use b::c::*;
pub use b::e as d;
}
Yes, module A = struct include B.C end creates an entirely new module and exports all definitions from B.C. All abstract types and data types that are imported from B.C are explicitly related to that module.
In other words, suppose you have
module Inner = struct
type imp = Foo
type t = int
end
so when we import Inner we can access the Inner definitions,
module A = struct
include Inner
let x : imp = Foo
let 1 : t = 1
end
and the Foo constructor in A belongs to the same type as the Foo constructor in the Inner module so that the following typechecks,
A.x = Inner.Foo
In other words, include is not a mere copy-paste, but something like this,
module A = struct
(* include Inner expands to *)
type imp = Inner.imp = Foo
type t = Inner.t = int
end
This operation of preserving type equalities is formally called strengthening and always applied when OCaml infers module type. In other words, the type system never forgets the type sharing constraints and the only way to remove them is to explicitly specify the module type that doesn't expose the sharing constraints (or use the module type of construct, see below).
For example, if we will define a module type
module type S = sig
type imp = Foo
type t = int
end
then
module A = struct
include (Inner : S)
end
will generate a new type foo, so A.Foo = Inner.Foo will no longer type check. The same could be achieved with the module type of construct that explicitly disables module type strengthening,
module A = struct
include (Inner : module type of Inner)
end
will again produce A.Foo that is distinct from Inner.Foo. Note that type t will be still compatible in all implementation as it is a manifest type and A.t is equal to Inner.t not via a sharing constraint but since both are equal to int.
Now, you might probably have the question, what is the difference between,
module A = Inner
and
module A = struct include Inner end
The answer is simple. Semantically they are equivalent. Moreover, the former is not a module alias as you might think. Both are module definitions. And both will define a new module A with exactly the same module type.
A module alias is a feature that exists on the (module) type level, i.e., in the signatures, e.g.,
module Main : sig
module A = Inner (* now this is the module alias *)
end = struct
module A = Inner
end
So what the module alias is saying, on the module level, is that A is not only has the same type as Inner but it is exactly the Inner module. Which opens to the compiler and toolchain a few opportunities. For example, the compiler may eschew module copying as well as enable module packing.
But all this has nothing to do with the observed semantics and especially with the typing. If we will forget about the explicit equality (that is again used mostly for more optimal module packing, e.g., in dune) then the following definition of the module A
module Main = struct
module A = Inner
end
is exactly the same as the above that was using the module aliasing. Anything that was typed with the previous definition will be typed with the new definition (modulo module type aliases). It is as strong. And the following is as strong,
module Main = struct
module A = struct include Inner end
end
and even the following,
module Main : sig
module A : sig
type imp = Impl.imp = Foo
type t = Impl.t = int
end
end = struct
module A = Impl
end

Extending recursive modules

So when you define the structure of a module, it's possible to extend another module off of it:
module Base = struct
type t = Name of string
end
module Child = struct
include Base
end
Child.Name "test"
(* - : Child.t = Child.Name "test" *)
However, when working with recursive modules using recursive signatures, I run into issues when I try to extend a module:
module rec Base : sig
type t = | Name of string
end = Base
and Child : sig
include Base
end = Child
When I do this, I get an error saying:
Error: Unbound module type Base
Can you not extend modules when working with this recursive module trick? Am I misunderstanding something or doing something wrong?
It seems to me your problem is that Base is a module, not a module type. When including in a sig ... end construct you need a type. When including in a struct ... end construct you need a module. That's why the first example works and the second one doesn't.
If I change Base to module type of Base I get this error:
Error: Illegal recursive module reference
So I suspect this particular (somewhat strange) type of recursive definition isn't supported.
If you define the module type separately, you can make it work:
module type BASE = sig type t = Name of string end
module rec Base : BASE = Base
and Child : sig
include BASE
end = Child
Jeffrey Scofield already gave a good answer. I'd just like to add that include is just syntactic sugar. So if it is include which is causing you trouble, a solution might be to expand it. In the first of your examples that would lead to
module Base = struct
type t = Name of string
end
module Child = struct
type t = Name of string
end
Apparently, your examples are simplified versions of what you really want to do. As shown, there is no need to use rec at all. So I can only guess how much recursion you really need. According to Jeffrey Scofield's answer, the combination of rec and include is problematic. Possibly, getting rid of include suffices for you.

'How to use higher order functors properly?' or 'How to have serious fun with funsigs?'

Motivation
For the life of me, I cannot figure out how to use higher order functors in
SML/NJ to any practical end.
According to the
SML/NJ docs on the implementation's special features,
it should be possible to specify one functor as an argument to another by use of
the funsig keyword. Thus, given a signature
signature SIG = sig ... end
we should be able to specify a functor that will produce a module satisfying
SIG, when applied to a structure satisfying some signature SIG'. E.g.,
funsig Fn (S:SIG') = SIG
With Fn declared in this way, we should then (be able to define another
functor that takes this functor as an argument. I.e., we can define a module
that is parameterized over another parameterized module, and presumably use the
latter within the former; thus:
functor Fn' (functor Fn:SIG) =
struct
...
structure S' = Fn (S:SIG')
...
end
It all looks good in theory, but I can't figure out how to actually make use of
this pattern.
Example Problems
Here are two instances where I've tried to use this pattern, only to find
it impracticable:
First attempt
For my first attempt, just playing around, I tried to make a functor that would
take a functor implementing an ordered set, and produce a module for dealing
with sets of integers (not really useful, but it would let you parameterize sets
of a given type over different set implementations). I can define the
following structures, and they will compile (using Standard ML of New Jersey
v110.7):
structure IntOrdKey : ORD_KEY
= struct
type ord_key = int
val compare = Int.compare
end
funsig SET_FN (KEY:ORD_KEY) = ORD_SET
functor IntSetFn (functor SetFn:SET_FN) =
struct
structure Set = SetFn (IntOrdKey)
end
But when I actually try to apply IntSetFn to a functor that should satisfy the
SET_FN funsig, it just doesn't parse:
- structure IntSet = IntSetFn (functor ListSetFn);
= ;
= ;;
stdIn:18.1-24.2 Error: syntax error: deleting RPAREN SEMICOLON SEMICOLON
- structure IntSet = IntSetFn (functor BinarySetFn) ;
= ;
= ;
stdIn:19.1-26.2 Error: syntax error: deleting RPAREN SEMICOLON SEMICOLON
Second attempt
My second attempt fails in two ways.
I have defined a structure of nested modules implementing polymorphic and
monomorphic stacks (the source file, for the curious). To
implement a monomorphic stack, you do
- structure IntStack = Collect.Stack.Mono (type elem = int);
structure IntStack : MONO_STACK?
- IntStack.push(1, IntStack.empty);
val it = - : IntStack.t
and so forth. It seems to work fine so far. Now, I want to define a module that
parameterizes over this functor. So I have defined a funsig for the
Collect.Stack.Mono functor (which can be seen in my repo). Then, following the
pattern indicated above, I tried to define the following test module:
(* load my little utility library *)
CM.autoload("../../../utils/sources.cm");
functor T (functor StackFn:MONO_STACK) =
struct
structure S = StackFn (type elem = int)
val x = S.push (1, S.empty)
end
But this won't compile! I get a type error:
Error: operator and operand don't agree [overload conflict]
operator domain: S.elem * S.t
operand: [int ty] * S.t
in expression:
S.push (1,S.empty)
uncaught exception Error
raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:292.17-292.20
Yet, inside functor T, I appear to be using the exact same instantiation
pattern that works perfectly at the top level. What am I missing?
Unfortunately, that's not the end of my mishaps. Now, I remove the line
causing the type error, leaving,
functor T (functor StackFn:MONO_STACK) =
struct
structure S = StackFn (type elem = int)
end
This compiles fine:
[scanning ../../../utils/sources.cm]
val it = true : bool
[autoloading]
[autoloading done]
functor T(<param>: sig functor StackFn : <fctsig> end) :
sig
structure S : <sig>
end
val it = () : unit
But I cannot actually instantiate the module! Apparently the path access syntax
is unsupported for higher order functors?
- structure Test = T (functor Collect.Stack.Mono);
stdIn:43.36-43.43 Error: syntax error: deleting DOT ID DOT
I am at a lost.
Questions
I have three related questions:
Is there a basic principle of higher-order functors in SML/NJ that I'm
missing, or is it just an incompletely, awkwardly implemented feature of the
language?
If the latter, where can I turn for more elegant and practicable higher order
functors? (Hopefully an SML, but I'll dip into OCaml if necessary.)
Is there perhaps a different approach I should taking to achieve these kinds
of effects that avoids higher order functors all together?
Many thanks in advance for any answers, hints, or followup questions!
Regarding your first attempt, the right syntax to apply your IntSetFn functor is:
structure IntSet = IntSetFn (functor SetFn = ListSetFn)
The same applies to your application of the Test functor in the second attempt:
structure Test = T (functor StackFn = Collect.Stack.Mono)
That should fix the syntax errors.
The type error you get when trying to use your stack structure S inside functor T has to do with the way you defined the MONO_STACK funsig:
funsig MONO_STACK (E:ELEM) = MONO_STACK
This just says that it returns a MONO_STACK structure, with a fully abstract elem type. It does not say that its elem type is gonna be the same as E.elem. According to that, I would able to pass in a functor like
functor F (E : ELEM) = struct type elem = unit ... end
to your functor T. Hence, inside T, the type system is not allowed to assume that type S.elem = int, and consequently you get a type error.
To fix this, you need to refine the MONO_STACK funsig as follows:
funsig MONO_STACK (E:ELEM) = MONO_STACK where type elem = E.elem
That should eliminate the type error.
[Edit]
As for your questions:
Higher-order functors are a little awkward syntactically in SML/NJ because it tries to stay 100% compatible with plain SML, which separates the namespace of functors from that for structures. If that wasn't the case then there wouldn't be the need for funsigs as a separate namespace either (and other syntactic baroqueness), and the language of signatures could simply be extended to include functor types.
Moscow ML is another SML dialect with a higher-order module extension that resolves the compatibility issue somewhat more elegantly (and is more expressive). There also was (now mostly dead) ALice ML, yet another SML dialect with higher-order functors that simply dropped the awkward namespace separation. OCaml of course did not have this constraint in the first place, so its higher-order modules are also more regular syntactically.
The approach seems fine.

OCaml This function is applied to too many arguments

This is just a simple program I wrote to try to get a better understanding about modules. I'm trying to call the toS function with Id("a",Int) but it seems like I can write a type ast like this. What may be the problem?
module Typ =
struct
type typ = Int | Bool
end
module Symbol =
struct
type t = string
end
module Ast =
struct
type ast = Const of int * Typ.typ | Id of Symbol.t * Typ.typ
let rec toS ast = match ast with Id(a,b) -> "a"
|_->"b"
end
Ast.toS Id("a",Int)
You are getting an error because you did not surround your type constructor with parens in the function application. However, you must also reference type constructors by fully qualified names outside the module they're defined in. I.e.
Ast.toS (Ast.Id("a",Typ.Int))
Alternatively, you can open the modules. But this is considered bad practice. I.e.
open Id
open Typ
Ast.toS (Id("a",Int))

VB.NET: Operator '=' is not defined ... for a variable and object of THE SAME TYPE?

Okay, I am TOTALLY confused here. I have a class... say MyClass. It has several properties of another class of my type, say MyHelperClass (along with other properties).
I am doing the following:
Dim inst As MyClass = New MyClass() With {
.p1 = sv1,
.p2 = sv2,
.h1 = getHelperClass(a1),
.p3 = sv3,
.p4 = sv4,
.h2 = getHelperClass(a2),
.p5 = sv5,
...
.pN = svN
}
*where .p# is some property, .sv# is some valid value. .h# is a property of type MyHelperClass and getHelperClass(a#) returns an instance of said class.
Now, I have the odd thing here, where the assignment statement for h1 works perfect. No problems. The assignment statement for h2 however, it is giving me the following blue-squiggle error:
Operator '=' is not defined for types myLib.MyHelperClass and myLib.MyHelperClass.
I just do not get this error at all! I don't even know where to start to figure this out. HELP!
201105.06 0305:
The signature for h1's type is List(Of myLib.Address), where Address is a very basic class with typical address fields (name, address, city, state, zip, etc.). The return type of getHelperClass is also List(Of myLib.Address).
As SSS hinted at in his answer, I would expect = to not work in the "natural" way if I was using it for equality testing on a class without operators, however I am using it as an assignment operator, not equality, which I can't see any problem with. I am expecting the result of getHelperClass to be assigned to h2. But instead it's telling me = is not defined for the type. Is it possible that for some reason, the compiler is interpreting it as =(EQUALS) instead of =(ASSIGN)?
As for commenting out that line and it happening on the first one, I'll need to wait till I'm back in the office tomorrow to check that. Will report back.
Ah, yeah, sorry didn't read your OP properly. The assignment must be being misinterpreted as an comparison. Maybe you are missing a comma? For example in the statement "a = b = c" the first equals sign is an assignment, the second is a comparison.
You need to add the Operator methods to MyHelperClass
e.g.
Public Shared Operator =(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
Public Shared Operator <>(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
You should also read up on the difference between Reference and Value types.