val x = IntListSet.listItems(IntListSet.addList(IntListSet.empty,[1,2,3,4,5,6]));
If you run this in SML, it returns
x = [1,2,3,4,5,6] : IntListSet.item list
I want it to return
x = [1,2,3,4,5,6] : Int list
What should I do?
You need not do anything. IntListSet.item is just an alias for Int.int, so your function already returns the correct type.
Related
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.
Some functions in the List module fail when the argument is an empty list. List.rev is an example. The problem is the dreaded Value Restriction.
I met the same problem while trying to define a function that returns a list with all but the last element of a list:
let takeAllButLast (xs: 'a list) =
xs |> List.take (xs.Length - 1)
The function works well with nonempty lists, but a version that would handle empty lists fails:
let takeAllButLast (xs: 'a list) =
if List.isEmpty xs then []
else xs |> List.take (xs.Length - 1)
takeAllButLast []
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : '_a list, etc.
I tried several things: making it an inline function, not specifying a type for the argument, specifying a type for the returned value, making the function depend on a type argument, and using the Option type to obtain an intermediate result later converted to list<'a>. Nothing worked.
For example, this function has the same problem:
let takeAllButLast<'a> (xs: 'a list) =
let empty : 'a list = []
if List.isEmpty xs then empty
else xs |> List.take (xs.Length - 1)
A similar question was asked before in SO: F# value restriction in empty list but the only answer also fails when the argument is an empty list.
Is there a way to write a function that handles both empty and nonempty lists?
Note: The question is not specific to a function that returns all but the last element of a list.
The function itself is completely fine. The function does not "fail".
You do not need to modify the body of the function. It is correct.
The problem is only with the way you're trying to call the function: takeAllButLast []. Here, the compiler doesn't know what type the result should have. Should it be string list? Or should it be int list? Maybe bool list? No way for the compiler to know. So it complains.
In order to compile such call, you need to help the compiler out: just tell it what type you expect to get. This can be done either from context:
// The compiler gleans the result type from the type of receiving variable `l`
let l: int list = takeAllButLast []
// Here, the compiler gleans the type from what function `f` expects:
let f (l: int list) = printfn "The list: %A" l
f (takeAllButLast [])
Or you can declare the type of the call expression directly:
(takeAllButLast [] : int list)
Or you can declare the type of the function, and then call it:
(takeAllButLast : int list -> int list) []
You can also do this in two steps:
let takeAllButLast_Int : int list -> int list = takeAllButLast
takeAllButLast_Int []
In every case the principle is the same: the compiler needs to know from somewhere what type you expect here.
Alternatively, you can give it a name and make that name generic:
let x<'a> = takeAllButLast [] : 'a list
Such value can be accessed as if it was a regular value, but behind the scenes it is compiled as a parameterless generic function, which means that every access to it will result in execution of its body. This is how List.empty and similar "generic values" are implemented in the standard library.
But of course, if you try to evaluate such value in F# interactive, you'll face the very same gotcha again - the type must be known - and you'll have to work around it anyway:
> x // value restriction
> (x : int list) // works
I have a pretty simple code that's supposed to transform a list of tuples (int * string), into two lists, one list of ints and one list of strings - basically a list of tuples into a tuple of lists.
fun unzip_single_int[] : int list = []
| unzip_single_int(x::xs) : int list =
x :: unzip_single_int(xs)
fun unzip_single_string[] : string list = []
| unzip_single_string(x::xs) : string list =
x :: unzip_single_string(xs)
fun unzip[] : (int list * string list) = ([], [])
| unzip([twopls]) : (int list * string list) =
let
val x : int list = unzip_single_int(twopls);
val y : string list = unzip_single_string(twopls); (* this is line 28 btw *)
in
(x, y)
end
And the error:
zip.sml:28.7-28.52 Error: operator and operand don't agree [tycon mismatch]
operator domain: string list
operand: int list
in expression:
unzip_single_int twopls
For some reason the compiler believes val y : string list = unzip_single_string(twopls) is referring to an int list.
Interestingly enough, when I switch the two around, when I change:
val x : int list = unzip_single_int(twopls);
val y : string list = unzip_single_string(twopls);
to
val y : string list = unzip_single_string(twopls);
val x : int list = unzip_single_int(twopls);
The error switches too:
zip.sml:28.7-28.47 Error: operator and operand don't agree [tycon mismatch]
operator domain: int list
operand: string list
in expression:
unzip_single_int twopls
For some reason, whatever the second call is, it's going to assume that its whatever the last call's type was. Why is it doing this? How do I overcome this? I made it very clear in the two other functions definitions that they are int lists and string lists respectively, so why does SML think that I'm sending it a string list when I clearly defined it as an int list ?
Thanks!
The answer to your question is type inference. You haven't given any type for twopls or an argument type for unzip, and you are calling unzip_single_int(twopls) when you make a value declaration for x. Therefore, SML infers that twopls is an int list, because the type of your function unzip_single_int is int list -> int list, which means it takes an int list as input and returns an int list.
After SML infers that twopls is an int list, you are trying to call unzip_single_string(twopls). However, the type of the function unzip_single_string is string list -> string list, therefore it expects an expression of string list as an input. But now twopls is an int list, so you get a type error.
However, your function definitions don't change anything about the list, they return the exact same list, I'm guessing you want them to return either the first or the second element in the tuple, so you should add that. Your unzip function is supposed to have the type (int * string) list -> int list * string list. Therefore, try to pattern match on the int * string elements you have in your list. Normally you would pattern match a list like x :: xs, but if you know the elements are tuples and if you want to access them, you can pattern match them like (num, str) :: xs, where num is an int variable and str is a string variable. You should be able to figure out the rest. Good luck!
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.
fun in_list (x : int, y : int list) =
if null y
then false
else if x=hd y then true
else in_list(x,tl y)
This is what my code currently looks like, it simply returns true if x appears in the the y list, false if not. The issue is I want it to be able to input "a" and ["a", "b", "c"] as well, or even have x be a list, and y be a list of lists. I am VERY new to ML (just started learning about it last week), and have searched for answers and cannot come up with anything.
If you change the first line to
fun in_list (x : ''a, y : ''a list) =
then it will behave as you want. Here ''a is an equality type variable that can stand for any type that supports the = operator.
You could also just leave out the types altogether and the most general correct type will be inferred.
Types can always be omitted in function declarations, with is only one exception: where overloading of operators could cause ambiguity. An example is
fun square x = x*x
because the type of x could be either int or real. (It will default to int, maybe not what you want.)
The point is that there is only one function hd. But the operator * can refer to two quite different functions.