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)
Related
I got stuck at this problem where I had to :
define a type date as a triple where the first component is a year represented as an
integer number, the second component is an integer number from the interval [1..12]
representing the month, and the last component represents a day with an integer
number.
type date = { year: int; month: int; day:int};;
{year = 2012; month = 12; day = 21};;
This works fine but for parameter month I need to have integers from 1 to 12 as an input.
If you want for months only use a variant and enumerate all the month. You could do the same for day number but I would avoid it.
In case you wanted a more general notion like range type: you don't want that because month have either 30 or 31 days and February have 28-29 depending on leap year so it is dependent range... If this check is available which range do you want then ? The check you require is very specific: as an example I have only seen this in cpp with a extension and your forced to use int from the scope (so very unuseful). Because this extension required static int you couldn't really use for date. OCaml can't do that because all int are int not 30 nor 31. The usual workaround is create a function that take three int verify if they are in a valid range and return date option and also make it private from outside so you can't break the invariant. We call this smart constructor.
module SafeDate : sig
type date = private { year: int; month: int; day:int}
val create: int -> int -> int -> date option
end = struct
type date = { year: int; month: int; day:int}
let create year month day = if (* put formula/code to say if correct *) then Some{year; month; day} else None
end
No, but you can prevent accidents.
Time and date libraries in OCaml (and elsewhere) usually ensure that the components of date objects fall in correct ranges without special type-system trickery.
In OCaml, a good solution may be to expose the fields of the date as read-only. This means the user of the module won't be able to create a record of such type using the notation {year=...; month=...; day=...}. This is achieved by providing an explicit module interface (.mli file) which declares the date type as private:
(* Date.mli *)
type t = private {
year: int;
month: int;
day: int;
}
(** Create a date object if and only if given acceptable values *)
val create : year:int -> month:int -> day:int -> t option
The implementation (Date.ml file) is as usual. In particular, there's no private keyword in the type definition:
(* Date.ml *)
type t = {
year: int;
month: int;
day: int;
}
let create ...
You can also get creative with this. For example, you can have the library safely expose fields derived from year/month/day, such as the number of days since the beginning of the year etc.
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
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.
So I've made a class, one of it's function returns a struct vector, like so:
vector<highscore::players> highscore::returnL(){
load();
return list;
}
So list is basically,
struct players {
string name;
int score;
};
vectors<players> list;
In my source cpp, I tried to capture this vector, so I made another struct and struct vector.
Source.cpp:
struct players1 {
string name;
int score;
};
vector<players1> highscorelist;
Then I tried to
highscore high; //class' name is highscore
highscorelist = high.returnL();
But I get the error message:
No operator "=" matches these operands
" operand types are std::vector<players1, std::allocator<players1>> = std::vector<highscore::players, std::allocator<highscore::players>> "
Is it not possible to do it this way?
I honestly don't know what to search for so this might have been answered before, apologize if that's the case.
You could use reinterpret_cast, but that's not a good solution. Why don't you use highscore::player?
std::vector<highscore::player> highscoreList;
highscoreList = high.returnL(); // ok
highscore::player and player1 are different types, even though they have the same variables and probably even the same memory layout. You cannot just interchange types like that. Also, if you change one of those types, you have to change the other, which is just a maintenance nightmare if it were possible.
If you can, you could also use auto:
auto highscoreList = high.returnL();
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)).