In standard ML (Standard ML of New Jersey), we use following syntax to construct tuple
val x = (1, 2);
val u = ();
However we can not construct tuple with only one element
val x = (1); (* normal int *)
val y = (1,); (* python syntax, not valid in SML *)
On the other hand, one element tuple and element itself seems have same type signature.
Can we distinct 'a and a tuple with only one element of type 'a in SML?
If so, how can we construct a one element tuple and what's type signature of it?
Can we distinct 'a and a tuple with only one element of type 'a in SML?
Yes, you can.
Unlike Python, there isn't any special (1,) syntax. But since tuples are equivalent to records with numbered fields, you can create a record with exactly one field named 1 and access it using the #1 macro for getting the first value of a tuple:
- val foo = { 1 = 42 };
val foo = {1=42} : {1:int}
- #1 foo;
val it = 42 : int
You can see that this is actually a 1-tuple by trying to annotate a regular 2-tuple as a record:
- (3.14, "Hello") : { 1 : real, 2 : string };
val it = (3.14,"Hello") : real * string
what's type signature of it?
The type would be { 1 : 'a }. You can preserve the type parameter like this:
type 'a one = { 1 : 'a };
You could get something similar using a datatype:
datatype 'a one = One of 'a
fun fromOne (One x) = x
I think those would use the same amount of memory.
Related
I'm working on a bloom filter in OCaml and I'm really stumped.
First, I define a signature to interact with a bloom filter, so that the bloom filter can be implemented in multiple different ways:
module type memset = sig
type elt (* type of values stored in the set *)
type t (* abstract type used to represent a set *)
val mem : elt -> t -> bool
val empty : t
val is_empty : t -> bool
val add : elt -> t -> t
val from_list : elt list -> t
val union : t -> t -> t
val inter : t -> t -> t
end
The bloom filter currently has two implementations:
SparseSet
SparseSet is implemented by storing all integers using a list.
module SparseSet : (memset with type elt = int) = struct
include Set.Make(struct
let compare = Pervasives.compare
type t = int
end)
let from_list l = List.fold_left (fun acc x -> add x acc) empty l
end
BoolSet
Implements the bloom filter by storing an array of booleans, where an integer is a member of the set if its corresponding index = true.
module BoolSet : (memset with type elt = int) = struct
type elt = int
type t = bool array
(* implementation details hidden for clarity's sake *)
end
In order to store whether an item exists in a set or not that isn't an integer, I define a hasher signature:
module type hasher = sig
type t (* the type of elements that are being hashed *)
val hashes : t -> int list
end
Finally, I define a Filter functor that accepts a bloom filter implementation and a hasher. To add an item, an item is hashed using three different methods to produce 3 integers. The three integers are stored in the underlying memset module passed to the Filter functor. To check if an item exists in the set, its 3 hashes are obtained, and checked. If all three hash integers exist in the set, the item is contained in the set. The filter functor allows the implementation of the bloom set, and the hash method to be swapped out:
module Filter (S : memset) (H : hasher)
: memset
with type elt = H.t
with type t = S.t = struct
type elt = H.t
type t = S.t
let mem x arr = [] = List.filter (fun y -> not (S.mem y arr)) (H.hashes x)
let empty = S.empty
let is_empty = S.is_empty
let add x arr = List.fold_left (fun acc x -> S.add x acc) empty (H.hashes x)
let add x arr = empty
let from_list l = S.from_list l
let union l1 l2 = S.union l1 l2
let inter l1 l2 = S.inter l1 l2
end
When I try to compile this program I get the following compile-time error that occurs at the mem, add, and from_list functions in the Filter functor:
File "bloom.ml", line 75, characters 66-78:
Error: This expression has type int list but an expression was expected of type
S.elt list
Type int is not compatible with type S.elt
For some reason the type isn't getting passed through correctly in the Filter module. Anyone have any suggestions on how to fix this? I've been tearing my hair out trying to figure it out.
The line
module Filter (S : memset) (H : hasher) = ...
means that the functor should work for any memset, independently of the type of elements S.elt. However, the functor body assumes that S.elt is int, leading to the type error:
Type int is not compatible with type S.elt
You can fix this issue by precising the type of S.elt in the argument signature:
module Filter (S : memset with type elt = int) (H : hasher) = ...
I would like to represent some scalar value (e.g. integers or strings)
by either it's real value or by some NA value and later store them
in a collection (e.g. a list). The purpose is to handle missing values.
To do this, I have implemented a signature
module type Scalar = sig
type t
type v = Value of t | NA
end
Now I have some polymorphic Vector type in mind that contains Scalars. Basically, some of the following
module Make_vector(S: Scalar) = struct
type t = S.v list
... rest of the functor ...
end
However, I cannot get this to work. I would like to do something like
module Int_vector = Make_vector(
struct
type t = int
end
)
module Str_vector = Make_vector(
struct
type t = string
end
)
... and so on for some types.
I have not yet worked a lot with OCaml so maybe this is not the right way. Any advises on how to realize such a polymorphic Scalar with a sum type?
The compiler always responds with the following message:
The parameter cannot be eliminated in the result type.
Please bind the argument to a module identifier.
Before, I have tried to implement Scalar as a sum type but ran into
complexity issues when realizing some features due to huge match clauses. Another (imo not so nice) option would be to use option. Is this a better strategy?
As far as I can see, you are structuring v as an input type to your functor, but you really want it to be an output type. Then when you apply the functor, you supply only the type t but not v. My suggestion is to move the definition of v into your implementation of Make_vector.
What are you trying to do exactly with modules / functors? Why simple 'a option list is not good enough? You can have functions operating on it, e.g.
let rec count_missing ?acc:(acc=0) = function
| None::tail -> count_missing ~acc:(acc+1) tail
| _::tail -> count_missing ~acc tail
| [] -> acc ;;
val count_missing : ?acc:int -> 'a option list -> int = <fun>
count_missing [None; Some 1; None; Some 2] ;;
- : int = 2
count_missing [Some "foo"; None; Some "bar"] ;;
- : int = 1
I have a question about the way SML of New Jersey interprets lists:
Suppose I have a function f(x : 'a, n : int) : 'a list such that f returns a list of n copies of x, e.g. f(2,5) = [2,2,2,2,2], f(9,0) = [].
So then I go into the REPL, and I check f(9,0) = nil, and it returns true. From this, I assumed that you could use list = nil to check whether a list is the empty list. I used this in a function, and it wouldn't run. I ended up learning that the type definitions are different:
sml:121.2-123.10 Error: operator and operand don't agree [equality type required]
operator domain: ''Z * ''Z
operand: 'a list * 'Y list
in expression:
xs = nil
(Where xs was my list). I then learned that the way to check if a list is the empty list is with null list. Why is this so? What's going on with nil? Can someone explain this behavior to me?
I also note that apparently (case xs = of nil is the same as checking null xs. Does this mean nil is a type?
This is an error related to polymorphism. By default, when an empty list is evaluated, it has the type 'a list. This means that the list can contain elements of any type. If you try to evaluate 1::[], you won't get a type error because of that. This is called polymorphism, it is a feature that allows your functions to take arguments of any type. This can be useful in functions like null, because you don't care about the contents of the list in that case, you only care about its length (in fact, you only care if it's empty or not).
However, you can also have empty lists with different types. You can make your function return an empty int list. In fact, you are doing so in your function.
This is the result in a trivial implementation of your function:
- fun f(x : 'a, n : int) : 'a list =
case n of
0 => []
| _ => x::f(x, n-1);
val f = fn : 'a * int -> 'a list
- f(4,5);
val it = [4,4,4,4,4] : int list
- f(4,0);
val it = [] : int list
As you can see, even if the second argument is 0, your function returns an int list. You should be able to compare it directly with an list of type 'a list.
- it = [];
val it = true : bool
However, if you try to compare two empty lists that have different types and are not type of 'a list, you should get an error. You can see an example of it below:
- [];
val it = [] : 'a list
- val list1 : int list = [];
val list1 = [] : int list
- val list2 : char list = [];
val list2 = [] : char list
- list1 = [];
val it = true : bool
- list2 = [];
val it = true : bool
- list1 = list2;
stdIn:6.1-6.14 Error: operator and operand don't agree [tycon mismatch]
operator domain: int list * int list
operand: int list * char list
in expression:
list1 = list2
Also, case xs of nil is a way of checking if a list is empty, but this is because nil (which is just a way to write []) has the type 'a list by default. (Note that case expressions don't directly return a boolean value.) Therefore, nil is not a type, but 'a list is a polymorphic type that you can compare with lists of any type, but if your empty lists don't have polymorphic type, you will get a type error, which I think what is happening in your case.
using ML as a programming language we have list and tuple, in the case of lists we can form a list from another list by removing or appending elements from and to the original list, for example if we have:
val x = [7,8,9] : int list
in REPL we can do some operations like the following:
- hd x;
val it = 7 : int
- tl x;
val it = [8,9] : int list
now if we have a tuple lets say:
val y = (7,8,9) :int*int*int
now the question is that , can we have a smaller tuple by removing the first element from the original tuple ? in other words , how to remove (#1 y) and have new tuple (8,9) in a similar way that we do it in the case of list.
Thanks.
Tuples are very different from lists. With lists, size need not be known at compile time, but with tuples, not only should the number of elements be known at compile time, the type of each element is independent of the others.
Take the type signature of tl:
- tl;
val it = fn : 'a list -> 'a list
It is 'a list -> 'a list - in other words tl takes a list of 'a and returns another one. Why don't we have one for tuples as well? Assume we wanted something like
y = (1,2,3);
tail y; (* returns (2,3) *)
Why does this not make sense? Think of the type signature of tail. What would it be?
In this case, it would clearly be
'a * 'b * 'c -> 'b * 'c
Takes product of an 'a, a 'b and a 'c and returns a product of
a 'b and a 'c. In ML, all functions defined must have a statically determined
type signature. It would be impossible to have a tail function for tuples that
handles all possible tuple sizes, because each tuple size is essentially a different type.
'a list
Can be the type of many kinds of lists: [1,2,3,4], or ["A", "short", "sentence"], or
[true, false, false, true, false]. In all these cases, the value of the type
variable 'a is bound to a different type. (int, string, and bool). And 'a list can be a list of any size.
But take tuples:
(1, true, "yes"); (* (int * bool * string) *)
("two", 2) (* (string, int) *)
("ok", "two", 2) (* (string, string, int) *)
Unlike list, these are all of different types. So while the type signature of all lists is simple ('a list), there is no 'common type' for all tuples - a 2-tuple has a different type from a 3-tuple.
So you'll have to do this instead:
y = (7, 8, 9);
(a, b, c) = y;
and a is your head and you can re-create the tail with (b,c).
Or create your own tail:
fun tail (a,b,c) = (b, c)
This also gives us an intuitive understanding as to why such a function would not make sense: If is impossible to define a single tail for use across all tuple types:
fun tail (a,b) = (b)
| tail (a,b,c) = (b, c) (* won't compile *)
You can also use the # shorthand to get at certain elements of the tuple:
#1 y; (* returns 7 *)
But note that #1 is not a function but a compile time shorthand.
Lists and tuples are immutable so there is no such thing like removing elements from them.
You can construct a new tuple by decomposing the original tuple. In SML, the preferred way is to use pattern matching:
fun getLastTwo (x, y, z) = (y, z)
If you like #n functions, you can use them as well:
val xyz = (7, 8, 9)
val yz = (#2 xyz, #3 xyz) (* (8, 9) *)
Can someone explain the difference between them?
Also, when trying out:
datatype exp = Const of real | Pair of exp * exp;
val my_exp_2 = Pair(Const(1.2),Pair(Const(9.0),Const(2.0)));
The interpreter gives:
val my_exp_2 = Pair (Const 1.2,Pair (Const #,Const #)) : exp
Why does the # symbol appear there?
Thanks!
A recursive datatype is a datatype, which uses itself in its definition.
An example of this could be:
datatype intlist = IntNil
| IntCons of int * intlist
Notice how intlist is used in the definition of the IntCons value constructor.
val ls = IntCons(5, IntCons(6, IntNil));
Notice how IncCons contains another list value in this example.
A polymorphic datatype is a datatype, where one or more of the value constructors can contain a polymorphic value.
For instance, you could look at:
datatype 'a pair = Pair of 'a * 'a
Here, 'a is a type variable, and as such the constructor can be used on values of any type. Example:
val pairInt = Pair(1, 5);
val pairStr = Pair("Hello", "Goodbye");
val pairChr = Pair(#"x", #"y");
These two things are often combined into polymorphic recursive datatypes, as is done for normal lists:
datatype 'a mylist = MyNil
| MyCons of 'a * 'a mylist;
This is both polymorphic and recursive, as can be seen in these examples:
val listInt = MyCons(5, MyCons(6, MyNil));
val listStr = MyCons("abc", MyCons("def", MyNil));
val listChr = MyCons(#"a", MyCons(#"b", MyNil));