How to fix my method toString in class worker? - oop

I have some problems with printing types.
For example, define
type experience = Beginner | Experienced;;
class worker e =
object(self)
val exp : experience = e
method toString = let type_printer = function
| Beginner -> "Beginner"
| Experienced -> "Experienced" in
print_string ("Worker with experience " ^ (type_printer exp))
end;;
So I want to print the type I defined, among other things. When I called my method, nothing happened. I assume that the problem is in helper function. How to fix it?

I fixed a typo in your code (print_sting => print_string).
After this change, your code works for me.
# (* Enter the above code *)
class worker : experience ->
object val exp : experience method toString : unit end
# let w0 = new worker Beginner;;
val w0 : worker = <obj>
# w0#toString;;
Worker with experience Beginner- : unit = ()
It might help if you show a complete interaction with your code that fails in the way you're seeing.

Related

Why does the order of operands affect scope?

I'm trying to understand why a certain constructor is accepted in one expression but not another. I would have expected it to be out of scope in both. I'm a rank beginner to OCaml (I mostly use Haskell), so I could be missing something totally obvious to someone experienced.
type zero = Zero
type 'n succ = Succ
type 'n snat =
| SZero : zero snat
| SSucc : 'm snat -> 'm succ snat
module SimpleInduction (Pred : sig type 'n pred end) = struct
open Pred
type hyps =
{ base : zero pred
; step : 'm. 'm pred -> 'm succ pred}
let rec induct : type n. hyps -> n snat -> n pred =
fun h sn -> match sn with
| SZero -> h.base
| SSucc p -> h.step (induct h p)
end;;
let module Snot = struct type 'n pred = Top end in
let module Goop = SimpleInduction(Snot) in
Goop.induct {base = Top; step = fun _ -> Top} SZero = Top;;
(*
let module Snot = struct type 'n pred = Top end in
let module Goop = SimpleInduction(Snot) in
Top = Goop.induct {base = Top; step = fun _ -> Top} SZero;;
*)
This compiles just fine, for some reason. With the second definition of Snot uncommented, I get an error:
19 | Top = Goop.induct {base = Top; step = fun _ -> Top} SZero;;
^^^
Error: Unbound constructor Top
What brings Top into scope in the first definition of Snot? Using regular modules rather than first-classlocal ones makes no difference.
If I use Snot.Top on the left-hand side, I get no complaints on the right-hand side. Why is that?
In short, type-directed disambiguation is indeed not restricted to scope.
With an explicit type annotation, the type checker can select the constructor from the type without bringing the constructor in scope.
For instance,
module M = struct type 'a t = A of 'a end
let ok: _ M.t = A ()
let wrong: _ M.t = A (A ())
the first example is valid because the type annotation is enough to know that the A in A () is an _ A.t. However, the second example does not work because the constructor has not been brought into the scope.
Moreover, type-directed disambiguation only requires the expected type of the constructor or record to be known. Typically, in this example
let expected =
let f (M.A x) = x in
f (A ())
we know that the type of the argument of f is an _ M.t, thus we know that the A in f (A ()) come from _ M.t and we can use type-directed disambiguation like in the case with the explicit annotation.
If you find this behavior exotic, the warning 42 [name-out-of-scope] can be used to warn in such situation. Compiling your example with this warning yields (among many other instances of this warning)
23 | Goop.induct {base = Top; step = fun _ -> Top} SZero = Top
^^^
Warning 40 [name-out-of-scope]: Top was selected from type Snot.pred.
It is not visible in the current scope, and will not
be selected if the type becomes unknown.
(the warning names are new in 4.12)
Concerning your second point, the order of expression may matter in the absence of explicit annotations. Indeed, without explicit annotation, type-directed disambiguation will be only be able to select the right constructor when the expected type is already known. And type checking goes from left to right in OCaml. Thus in
... = Top
the type of the left-hand side has already been inferred and thus the expected type of Top is _ Snot.pred.
When the order is reversed
Top = ...
the typechecker is trying to find a constructor Top without any type information and there are no constructor Top in scope. Thus it fails with an Unbound constructor error. If you want to avoid depending on the order, you can either
write the full name of the constructor:
Snot.Top = ...
use an explicit type annotation
(Top: _ Snot.pred) = ...
open the Snot module.
Snot.( Top ) = ...
(* or *)
let open Snot in Top = ...
I would advise to use one of those solutions since there are more robust.
After all, relying on the specific implementation of the type checking is brittle.
In fact, there is a compiler flag -principal and a warning (18) [not-principal] that takes care to emit a warning in presence of such potentially brittle inference:
23 | Goop.induct {base = Top; step = fun _ -> Top} SZero = Top
^^^
Warning 18 [not-principal]: this type-based constructor disambiguation is not principal.
Here "not principal" means that the result of the type-based disambiguation depended on the order of the type-checking.

The signature for this packaged module couldn't be inferred in recursive function

I'm still trying to figure out how to split code when using mirage and it's myriad of first class modules.
I've put everything I need in a big ugly Context module, to avoid having to pass ten modules to all my functions, one is pain enough.
I have a function to receive commands over tcp :
let recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan = ...
After hours of trial and errors, I figured out that I needed to add (type a) and the "explicit" type chan = a to make it work. Looks ugly, but it compiles.
But if I want to make that function recursive :
let rec recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan =
Ctx.readMsg chan >>= fun res ->
... more stuff ...
|> OtherModule.getStorageForId (module Ctx)
... more stuff ...
recvCmds (module Ctx) nodeid chan
I pass the module twice, the first time no problem but
I get an error on the recursion line :
The signature for this packaged module couldn't be inferred.
and if I try to specify the signature I get
This expression has type a but an expression was expected of type 'a
The type constructor a would escape its scope
And it seems like I can't use the whole (type chan = a) thing.
If someone could explain what is going on, and ideally a way to work around it, it'd be great.
I could just use a while of course, but I'd rather finally understand these damn modules. Thanks !
The pratical answer is that recursive functions should universally quantify their locally abstract types with let rec f: type a. .... = fun ... .
More precisely, your example can be simplified to
module type T = sig type t end
let rec f (type a) (m: (module T with type t = a)) = f m
which yield the same error as yours:
Error: This expression has type (module T with type t = a)
but an expression was expected of type 'a
The type constructor a would escape its scope
This error can be fixed with an explicit forall quantification: this can be done with
the short-hand notation (for universally quantified locally abstract type):
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m
The reason behind this behavior is that locally abstract type should not escape
the scope of the function that introduced them. For instance, this code
let ext_store = ref None
let store x = ext_store := Some x
let f (type a) (x:a) = store x
should visibly fail because it tries to store a value of type a, which is a non-sensical type outside of the body of f.
By consequence, values with a locally abstract type can only be used by polymorphic function. For instance, this example
let id x = x
let f (x:a) : a = id x
is fine because id x works for any x.
The problem with a function like
let rec f (type a) (m: (module T with type t = a)) = f m
is then that the type of f is not yet generalized inside its body, because type generalization in ML happens at let definition. The fix is therefore to explicitly tell to the compiler that f is polymorphic in its argument:
let rec f: 'a. (module T with type t = 'a) -> 'never =
fun (type a) (m:(module T with type t = a)) -> f m
Here, 'a. ... is an universal quantification that should read forall 'a. ....
This first line tells to the compiler that the function f is polymorphic in its first argument, whereas the second line explicitly introduces the locally abstract type a to refine the packed module type. Splitting these two declarations is quite verbose, thus the shorthand notation combines both:
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m

expose a private type for module extension in OCaml

I'd like to extend a module but I need access to its private components. Here's an example:
nat.mli:
type t
val zero : t
val succ : t -> t
nat.ml:
type t = int
let zero = 0
let succ x = x + 1
I'd like to define a new module Ext_nat that defines a double function. I was trying to do something like this.
ext_nat.mli:
include (module type of Nat)
val double : t -> t
ext_nat.ml:
include Nat
let double x = 2 * x
It's not working as I don't have access to the representation of x in the last line.
Now that I'm thinking about this, it may not be such a good idea anyway because this would break the encapsulation of nat. So what is the best way to do this? I could define a new module nat_public where type t = int in the signature, and define nat and ext_nat with a private type t. What do you think?
You need to use with type statement. It is possible to write the code below in many different ways, but the idea is always the same.
module type NatSig =
sig
type t
val zero : t
val succ : t -> t
end
module type ExtNatSig =
sig
include NatSig
val double : t -> t
end
module ExtNat : ExtNatSig =
struct
type t = int
let zero = 0
let succ = fun x -> x + 1
let double = fun x -> x * 2
end
module Nat = (ExtNat : NatSig with type t = ExtNat.t)
let z = Nat.zero
let _ = ExtNat.double z
The problem is that as far as I remember it's impossible to achieve this behavior with your file structure: you define your module implicitly with signature in .mli file and structure itself in .ml, so you don't have enough control over you module, that's why I suggest you to reorganize your code a little bit (if it's not a problem).

use first-class module in OCaml

module type Arity =
sig
val arity : nat (* in my real code it has another type *)
end
module S =
functor (A : Arity) -> struct
let check = ...
end
I would like to use the function check inside the functor S without implement signature Arity. I read the first-class module but still not understand how to write it (in practice). Here is my draft code:
let A = has type of (module Arity)
then
let M = S (A)
then I can call check function by
M.check
I tried:
let f arity = (module (val arity : Arity) : Arity)
it returns : val f : (module Arity) -> (module Arity)
Could you please help me to write this first-class module? Am I able to write it in Ocaml?
Also in (http://caml.inria.fr/pub/docs/manual-ocaml-4.00/manual021.html#toc81) section 7.14 it says :
"The module expression (val expr : package-type) cannot be used in the body of a functor,..."
I am not understand it. Could you please help me understand by giving an example?
Thank you for your help.
I do not understand clearly what you want to know here. Apparently you get confused with some words of the normal OCaml modules and functors and, rather newer "first class modules" of OCaml. Anyway, I give you a short working example with OCaml 4.00.1 (do not try with 3.12.1 since things are improved in 4), probably it would help you:
module type Arity = sig
val arity :int
end
module S = functor (A : Arity) -> struct
let check = A.arity = 2 (* or whatever *)
end
The above is what you gave us with some trivial fixes to get compiled. Normally to use check, you give an implementation of signature Arity and give it to the functor S:
module AR = struct
let arity = 3
end
module SAR = S(AR)
let () = Printf.printf "%b\n" SAR.check
Let's use first class modules:
let a = (module AR : Arity)
This translates the module AR to a value and bind it to the variable a. Note that the parens are mandatory for syntax. You also need to give the siganture Arity. You can also write as follows:
let a' : (module Arity) = (module AR)
So the type of a and a' are (module Arity) and you need to give it to the compiler somehow. Unfortunately the type inference does not help us here.
You can make the value back to a module as follows:
module A' = (val a)
Now you can also make a first class module value of the functor S:
module type RESULT = sig
val check : bool
end
let s (a : (module Arity)) =
let module A = (val a) in
let module SA = S(A) in
(module SA : RESULT)
What s does is: take a value, make it back to a module, apply the functor S to it, then make another value from the result of functor application. The singature RESULT is necessary for the conversion. You cannot write (module SA : sig val check bool end). I am not good at things around here, but the typing of first class module values are not structural but nominal, I heard. You need to give a name to the signature at (module M : X).
The s's type is (module Arity) -> (module RESULT). Let's apply s to a:
let m = s a
To access check inside m, you need to make it back a module:
let m_check =
let module M = (val m) in
M.check
You might be disappointed to see that value<->module conversions are explicit, but this is how it works...

Modules and record fields

I have stumbled across a rather simple OCaml problem, but I can't seem to find an elegant solution. I'm working with functors that are applied to relatively simple modules (they usually define a type and a few functions on that type) and extend those simple modules by adding additional more complex functions, types and modules. A simplified version would be:
module type SIMPLE = sig
type t
val to_string : t -> string
val of_string : string -> t
end
module Complex = functor (S:SIMPLE) -> struct
include S
let write db id t = db # write id (S.to_string t)
let read db id = db # read id |> BatOption.map S.of_string
end
There is no need to give the simple module a name because all its functionality is present in the extended module, and the functions in the simple module are generated by camlp4 based on the type. The idiomatic use of these functors is:
module Int = Complex(struct
type t = int
end)
The problem appears when I'm working with records:
module Point2D = Complex(struct
type t = { x : int ; y : int }
end)
let (Some location) = Point2D.read db "location"
There seems to be no simple way of accessing the x and y fields defined above from outside the Point2D module, such as location.x or location.Point2D.x. How can I achieve this?
EDIT: as requested, here's a complete minimal example that displays the issue:
module type TYPE = sig
type t
val default : t
end
module Make = functor(Arg : TYPE) -> struct
include Arg
let get = function None -> default | Some x -> (x : t)
end
module Made = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
let _ = (Made.get None).a (* <-- ERROR *)
Let's look at the signature of some of the modules involved. These are the signatures generated by Ocaml, and they're principal signatures, i.e. they are the most general signatures allowed by the theory.
module Make : functor (Arg : TYPE) -> sig
type t = Arg.t
val default : t
val get : t option -> t
end
module Made : sig
type t
val default : t
val get : t option -> t
end
Notice how the equation Make(A).t = A.t is retained (so Make(A).t is a transparent type abbreviation), yet Made.t is abstract. This is because Made is the result of applying the functor to an anonymous structure, so there is no canonical name for the argument type in this case.
Record types are generative. At the level of the underlying type theory, all generative types behave like abstract types with some syntactic sugar for constructors and destructors. The only way to designate a generative type is to give its name, either the original name or one that expands to the original name via a series of type equations.
Consider what happens if you duplicate the definition of Made:
module Made1 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
module Made2 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
You get two different types Made1.t and Made2.t, even though the right-hand sides of the definitions are the same. That's what generativity is all about.
Since Made.t is abstract, it's not a record type. It doesn't have any constructor. The constructors were lost when the structure argument was closed, for a lack of a name.
It so happens that with records, one often wants the syntactic sugar but not the generativity. But Ocaml doesn't have any structural record types. It has generative record types, and it has objects, which from a type theoretical view subsume records but in practice can be a little more work to use and have a small performance penalty.
module Made_object = Make(struct
type t = <a : int>
let default = object method a = 0 end
end)
Or, if you want to keep the same type definition, you need to provide a name for the type and its constructors, which means naming the structure.
module A = struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end
module MadeA = Make(A)
Note that if you build Make(A) twice, you get the same types all around.
module MadeA1 = Make(A)
module MadeA2 = Make(A)
(Ok, this isn't remarkable here, but you'd still get the same abstract types in MadeA1 and MakeA2, unlike the Made1 and Made2 case above. That's because now there's a name for these types: MadeA1.t = Make(A).t.)
First of all, in your last code sample, last line, you probably mean .a rather than .x.
The problem with your code is that, with the way you define your Make functor, the type t is abstract in Made: indeed, the functors use the TYPE signature which seals {a : int} as an abstract type.
The following design circumvent the issue, but, well, its a different design.
module type TYPE = sig
type t
val default : t
end
module Extend = functor(Arg : TYPE) -> struct
open Arg
let get = function None -> default | Some x -> (x : t)
end
module T = struct
type t = {a : int}
let default = { a = 0 }
end
module Made = struct
include T
include Extend(T)
end
let _ = Made.((get None).a)
The problem is that OCaml doesn't have a name to refer to the qualified components of the type t (in this case a record, but the same problem would be present with normal variants) outside Made. Naming the unnamed solves the problem:
module F = struct
type t = {a : int}
let default = { a = 0 }
end
module Made = Make(F)
let _ = (Made.get None).F.a (* <-- WORKS *)
You can also declare explicitly the type outside the functorial application:
type rcd = {a : int}
module Made = Make(struct
type t = rcd
let default = { a = 0 }
end)
let _ = (Made.get None).a (* <-- WORKS *)