Let's say I had a simple module MyFoo that looks something like this
module MyFoo = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
With this definition, it works great and as expected — I can do something like
let _ = MyFoo.A "";;
- : MyFoo.t = MyFoo.A ""
without any problems.
Now maybe I want to create a functor that consumes modules with this structure, so I define a module signature that describes generally what this looks like and call it BaseFoo
module type BaseFoo = sig
type t
val to_string : t -> string
end
If I redefine MyFoo the same way but giving it this signature like
module MyFoo : BaseFoo = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
I lose the precision of its type t (is there a better way to describe what happens here?) — for example:
let _ = MyFoo.A "";;
Error: Unbound constructor MyFoo.A
What exactly is going on here and why does it happen? Is there a canonical way for dealing with this kind of problem (besides just leaving off the signature)?
I've tried manually including the signature and the specific type type definition too but get a different kind of error (this probably isn't the right approach).
module MyFoo : sig
include BaseFoo
type t = | A of string | B of int
end = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
let _ = MyFoo.A "test";;
Error: Multiple definition of the type name t.
Names must be unique in a given structure or signature.
You don't need the signature
What is going on is pretty much what you describe: giving MyFoo the BaseFoo signature in its definition restricts it to the signature.
Why? Because this is what specifying a signature at this place is for. The canonical solution is to leave of the signature (usually, letting the signature definition next to the module definition will be clear enough for the reader).
Note that when you call MyFoo on your functor, the signature will be checked. My usual choice is to rely on that.
A few workarounds
Given what you've tried, I guess this could be interesting to you:
module type BaseFoo = sig ... end
module MyFoo = struct ... end
module MyFooHidden : BaseFoo = MyFoo (* Same as defining MyFoo : BaseFoo *)
module MyFooWithType :
BaseFoo with type t = MyFoo.t
= MyFoo (* What you want *)
The with type t = t' clause allows you to annotate a module signature to add type information to it. It is quite useful, especially when dealing with functors. See here for more information.
MyFooHidden may seem useless, but you can see it as a check that MyFoo has the right signature. You can still use MyFoo however you want after all. MyFooWithType is actually (a bit) less useful because if you change your signature to add a type you'd want exported, you'd need to add the export here too.
Using include
As for your include try. Well, nice try! You were almost there:
module MyFoo : sig
type t = A of string | B of int
include BaseFoo with type t := t
end
The with type t := t' is a bit different in that it doesn't perform an equality but a replacement. The type t definition is removed from the BaseFoo signature altogether and all instances are replaced with your own t, that way you don't get any double definition problem. See here for more details.
As you point out, this is probably not the approach you want, as you no longer easily know that MyFoo is indeed a BaseFoo.
Related
I am new to OCaml and I am trying use functors. When I use the module type annotations with the functors, this results in a compile time error in my code.
When I remove the : Printable (from the module FromToString line) and the : ToString (from the module IntToString line) annotations, the following program compiles without error:
module type ToString =
sig
type t
val to_string: t -> string
end
module type Printable =
sig
type t
val print: t -> unit
end
module FromToString (S:ToString) : Printable =
struct
type t = S.t
let print a = print_string ( S.to_string a)
end
module IntToString : ToString =
struct
type t = int
let to_string = string_of_int
end
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3
However, when I add these annotations (as shown in the code) the compiler gives the following error:
File "Functor.ml", line 26, characters 28-29:
Error: This expression has type int but an expression was expected of type
PrintableInt.t = FromToString(IntToString).t
How can I use these annotations without causing a compile error?
The root issue is that your signature constraints make the resulting module far too opaque. When you constrain your functor result:
module FromToString (S:ToString) : Printable = ...
you are making the type t an abstract type, which can only be consumed by the to_string function and never been produced. In other words, a module of type Printable is unusable by itself.
When starting with functor, it is very often useful to look at the module type inferred by the compiler for the resulting module.
In the FromToString case, this is:
module FromToString (S:ToString) : sig
type t = S.t
val print: t -> unit
end = ...
You can see that the inferred module type of the result
sig
type t = S.t
val print: t -> unit
end
that it is quite similar to Printable except that now the type t is equal to the type t of the argument module S.
Thus, it is possible to reuse Printable to write the full module type of the result by adding a type equality with a with constraint:
module FromToString (S:ToString): Printable with type t = S.t = struct
type t = S.t
let print a = print_string ( S.to_string a)
end
The same issue appears for IntToString and can be fixed in a similar way:
module IntToString : ToString with type t = int =
struct
type t = int
let to_string = string_of_int
end
Then the compiler error is gone:
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3
One has to make the type t public, using the with notation:
module type ToString =
sig
type t
val to_string: t -> string
end
module type Printable =
sig
type t
val print: t -> unit
end
module FromToString (S:ToString) : Printable with type t = S.t =
struct
type t = S.t
let print a = print_string ( S.to_string a)
end
module IntToString : ToString with type t =int =
struct
type t = int
let to_string = string_of_int
end
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3
I learned from this question that it's possible to use pattern matching with records. However, I've noticed that I'm having trouble trying to match different types of records.
My goal in this example is to be able to distinguish between different records. I'm given a record that I'm not entirely sure which type it is, and I'm trying to figure it out using pattern matching.
Here's a simplified example:
module IceCream = struct
type t = {
temperature: float;
toppings: string list;
}
end
module Candy = struct
type t = {
flavour: string;
colour: string;
volume: int;
}
end
(* Could be Candy or IceCream *)
let example =
{ Candy.
flavour = "mint";
colour = "green";
volume = 10 }
let printFavoriteTreat treat = match treat with
| { Candy.
flavour = "mint";
colour;
volume } -> "It's Candy"
| { IceCream.
temperature;
toppings } -> "It's IceCream"
let () = printFavoriteTreat example
When I try to build this file, I get:
Error: The field IceCream.temperature belongs to the record type IceCream.t
but a field was expected belonging to the record type Candy.t
Is doing something like this possible?
I'm given a record that I'm not entirely sure which type it is, and I'm trying to figure it out using pattern matching.
That is not possible. Types exist only at compile time, so checking what type it is at runtime is not possible.
In other words, in a valid program you can put a type annotation on every expression (in most cases you do not have to do that, though, thanks to type inference). If you cannot do that then you should design your program differently, such as using a sum type as others have suggested - in that case both values will have the same type (at compile time) but a different constructor (at run time).
The answer provided by Pierre is great, but the example not so much. (I've always hated examples named a, b...)
So, as Pierre suggests, you could define your types like this:
type ice_cream = {
temperature: float;
toppings: string
}
type candy = {
flavor: string;
color: string;
volume: int
}
Then, you can define a type treat as a variant of these two types:
type treat =
| Candy of candy
| IceCream of ice_cream
Then, using pattern-matching:
let print_favorite_treat = function
| Candy _ -> print_endline "You love candy!"
| IceCream _ -> print_endline "You love ice cream!"
You are trying to match against to different types without using variant types.
The module syntax you are using cannot help, as module just structure the code. You could have defined the following types :
type a = {
temperature: float;
toppings: string list;
}
type b = {
flavour: string;
colour: string;
volume: int;
}
But the results would be the same.
The way to disambiguate is to use variant types (or union type which is not described in the example below) :
let printFavoriteTreat treat = match treat with
| `A{ Candy.
flavour = "mint";
colour;
volume } -> "It's Candy"
| `B { IceCream.
temperature;
toppings } -> "It's IceCream"
;;
And
let () = printFavoriteTreat (`A example)
In Rascal, say I have the code:
value x = 2;
data Exp = con(int n);
Is there a way to call con(x), while x is a value (but actually an integer), without knowing on beforehand what the type of con's first argument is supposed to be (thus without explicitly casting it to an int)?
Why is it possible to call a function, say int something(int n) = n, with an integer defined as a value (e.g. value y = 2) passed into its first argument, while it gives me an error when I try to do the same with user-defined ADTs?
When you call a function in Rascal it actually is doing a pattern match on the arguments. So, if you define int something(int n) = n;, and then call something(x), it matches x with int n, sees that x is actually an int (so it can bind the value to n), and calls the function.
If you were to define value x = 2.5 instead and then call something(x) you would get an error since it cannot bind the value 2.5 to int n. You could overload something with a second definition that takes a real instead, like int something(real r) = toInt(r);, and it would then work. Two items to note here, though: something needs to return the same type in both cases, and you need to import util::Math to get access to toInt.
When you are using a constructor, like con(x), it doesn't do a pattern match for you automatically. The type that you give it has to match the type it expects. If you know that x will always be an int, it would be best to just declare it as such. Another option would be to create a function like Exp makeCon(int n) = con(n); which you could then use as you would like, i.e., Exp myExp = makeCon(x);. It would be best in this case to include a default version of the function, just in case you give it something unexpected, like default Exp makeCon(value x) { throw "Unexpected value <x>"; }, this way if you ever try to create a con with something that isn't an int you will get an error that you can handle, with the ability to create your own error message, add additional error handling versus just showing a message, see the value causing the problem, etc, versus just having the interpreter give an error (which may not give you all the info you want).
I want to have a big functor Hello(Blah: Blah_type) and save it in the file hello.ml, but how do I do this?
If I was just in my top level file, I'd have
module Hello(Blah: Blah_type) =
struct
val x = 2
end
but how do I put the argument in hello.ml? I can't just have the whole file being "val x = 2" ... ?
OCamlPro has a compiler patch and external tool which may support this:
http://www.ocamlpro.com/blog/2011/08/10/ocaml-pack-functors.html
As far as I know the official compiler release does not support .ml files as functors.
It is not possible. Source files are always represented as ordinary modules, not functors. This is trivially solved with one extra open.
To complement ygrek's answer with a real code sample, instead of a file foo.ml with content
module type S = sig
(* ... *)
end
module Hello (M : S) = struct
(* ... *)
end
module M : S = struct
(* ... *)
end
module H = Hello(M)
(* ... *)
You could have hello.ml with content
module type S = sig
(* ... *)
end
module Make (M : S) = struct
(* ... *)
end
and foo.ml rewritten as
module M : Hello.S = struct
(* ... *)
end
module H = Hello.Make(M)
(* ... *)
PS: In case you find it confusing, the module sealing M : S or M : Hello.S is optional (M will be coerced to this signature when passed to the functor anyway), it was just to show how this can be done.
I haven't worked with SML in awhile and I came across this line of code:
type memory = string -> int;
Does this define 'memory' to be a function which takes a string a returns an int, or something else entirely? I've searched for a similar declaration but I can't seem to find one or figure out what it does.
When I put it into SML/NJ, I just get this:
- type memory = string -> int;
type memory = string -> int
memory is not a function , it's just an abbreviation for a type that is a function that takes as input a string and returns an int.
So whenever you would like to write that something is of type string->int you can just write it's of type memory.
For example instead of writing:
- fun foo(f : string->int, s) = f s;
val foo = fn : (string -> int) * string -> int
you could write:
- fun foo( f: memory, s) = f s;
val foo = fn : memory * string -> int
Such type declarations can make your code more readable (e.g. instead of writing that a pair x is of type int*int like (x: int*int), you can just create an abbreviation type pair = int*int and then you can write that x is of type pair like this (x: pair)).