I'm trying to implement flatten : 'a list list -> 'a list list in SML.
I thought this should be relatively straight forward with higher order functions. My implementation is
val flatten = List.reduce (op #) []
However I'm getting a bizarre error message: "append.sml:1.6-1.36 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)". Thus when I try to flatten an int list list I get a type error:
:> flatten [[1,2],[3]];
stdIn:2.1-2.20 Error: operator and operand don't agree [literal]
operator domain: ?.X1 list list
operand: int list list
in expression:
flatten ((1 :: 2 :: nil) :: (3 :: nil) :: nil)
As the error message hints, you ran into the value restriction -- see here for an explanation. The solution is very simple: just "eta-expand" your definition, i.e., make the parameter explicit instead of relying on partial application:
fun flatten xs = List.reduce op# [] xs
Related
I got a problem that needs to turn a list of tuples into a flattened list for example:
[(1,2), (3,4), (5,6)] can be turned into [1,2,3,4,5,6]
I have tried to write a function like this:
fun helper2(nil,b) = []
| helper2(a,nil) = []
| helper2(a::l1,b::l2) =l1::l2
fun flatten2 [] = []
| flatten2 ((a,b)::tl) = helper2(a,b)
It shows:
val flatten2 = fn : ('a list * 'a list list) list -> 'a list list
And when I tried to run it using command flatten2[(1,2),(3,4),(5,6)];
It will give me the following error message:
stdIn:1.2-1.29 Error: operator and operand do not agree [overload conflict]
operator domain: ('Z list * 'Z list list) list
operand: ([int ty] * [int ty]) list
in expression:
flatten2 ((1,2) :: (3,4) :: (<exp>,<exp>) :: nil)
My questions are:
Why SML see the a and b values as lists, not just simply a and b
How can I revise my code so SML can see a and b as 'a and 'b not lists
How to make this code work the way it should be?
Thanks
First question: As to why the type comes out as ('a list * 'a list list) it's because type inference is looking at this part of the code:
| helper2(a::l1,b::l2) =l1::l2
^^
here
Keep in mind that the type of the "cons" (::) operator is 'a -> 'a list -> 'a list, it is gluing a single element onto a list of that same type of element. So SML has concluded that whatever l1 and l2 are, the relationship is that l2 is a list of whatever l1 is.
fun helper2(nil,b) = []
Says that a must be a list because nil has type 'a list. Therefore, l2 has to be a list of lists (of some type 'a).
Question 2 and 3: I'm not quite sure how to correct the code as it is written. I'd probably write something like this:
fun helper2 [] accum = List.rev accum
| helper2 ((a,b)::tl) accum = helper2 tl (b :: a :: accum);
fun flatten2 list = helper2 list [];
helper2 does all of the dirty work. If the input list is empty then we're all done and we can return the reversed accumulator that we've been building up. The second case is where we actually add things to the accumulator. We pattern match on the head and the tail of the list. This pattern match means that the input has type ('a * 'a) list (a list of tuples where both elements are the same type). In the head, we have a tuple and we name the first and second element a and b, respectively. We prepend a then b onto the accumulator and recursively call helper2 on the tail of the list. Eventually, we'll chew through all the elements in the list and then we'll be left with just the accumulator -- which, recall, has all the elements but in the reverse order. Calling List.rev reverses the accumulator and that's our answer.
And when I load and run it I get this:
- flatten2 [(1,2), (3,4), (5,6)];
val it = [1,2,3,4,5,6] : int list
Why SML see the a and b values as lists, not just simply a and b
Chris already answered this in-depth.
You're passing a as the first argument to helper2, which expects a list as its first argument. And you're passing b as the second argument to helper2, which uses its second argument, b::l2, also a list, as the tail of a list where a is the head. So b must be a list of those lists.
This doesn't make any sense, and is most likely a consequence of confusing syntax: You are passing in what you think of single elements a and b in flatten2, but when you deal with them in helper2 they're now lists where the heads are called a and b. Those are not the same a and b.
How can I revise my code so SML can see a and b as 'a and 'b not lists
You could ditch the helper function to begin with:
fun flatten2 [] = []
| flatten2 ((a,b)::pairs) = a :: b :: flatten2 pairs
The purpose of having a helper function is so that it can accumulate the result during recursion, because this version of flatten2 uses a lot of stack space. It can do this with an extra argument so that flatten2 doesn't need to mention it:
This is the version Chris made.
How to make this code work the way it should be?
You can make this code in a lot of ways. Two ways using explicit recursion were mentioned.
Here are some alternatives using higher-order functions:
(* Equivalent to my first version *)
fun flatten2 pairs =
foldr (fn ((a,b), acc) => a :: b :: acc) [] pairs
(* Equivalent to Chris'es version *)
fun flatten2 pairs =
rev (foldl (fn ((a,b), acc) => b :: a :: acc) [] pairs)
(* Yet another alternative *)
fun concatMap f xs =
List.concat (List.map f xs)
fun flatten2 pairs =
concatMap (fn (a,b) => [a,b]) pairs
Hi i am getting compilation errors in the following SML code, can someone help?
Error: operator and operand don't agree [UBOUND match]
operator domain: 'Z list
operand: ''list
in expression:
null mylist
stdIn:4.15-4.24 Error: operator and operand don't agree [UBOUND match]
operator domain: 'Z list
operand: ''list
in expression:
hd mylist
stdIn:6.19-6.36 Error: operator and operand don't agree [UBOUND match]
operator domain: 'Z list
operand: ''list
in expression:
tl mylist
stdIn:6.10-7.21 Error: operator is not a function [circularity]
operator: 'Z
in expression:
(exists_in (item,tl mylist)) exists_in
code:
fun exists_in (item: ''int, mylist:''list) =
if null mylist
then false
else if (hd mylist) = item
then true
else exists_in(item, tl mylist)
exists_in(1,[1,2,3]);
What each of the error messages is telling you is that you're applying a function to something of the wrong type. For example, null is of type 'a list -> bool, and so expects to be applied to an argument of type 'a list. In exists_in, you're applying null to something of type ''list on line 4.
SML also provides type inference, so there's no real need to specify the types of your arguments (though it can be useful for debugging). If you do want to specify the types, as molbdnilo commented, the types you are looking for are int and int list, or ''a and ''a list ('' a being a type variable for equality types).
Unrelatedly, perhaps a more idiomatic way to write your function is do define it by case analysis on the structure of your list, rather than using boolean checks. The advantage of this is that you are immediately given the data supporting the boolean check on whether or not the list is empty, rather than checking and then extracting the data. For example:
fun exists_in (item, []) = false
| exists_in (item, h::t) = (item = h) orelse exists_in (item, t)
I am trying to reverse a list in SML with the following implementation
fun reverse x y =
case x of
[] => y
| x::xs => reverse(xs, x::y)
;
The error messages I get is impenetrable:
trial.sml:1.6-4.35 Error: case object and rules don't agree [tycon mismatch]
rule domain: 'Z list * 'Z list
object: ('Z list * 'Z list) * 'Y
in expression:
(case (arg,arg)
of (x,y) =>
(case x
of nil => y
| :: <pat> => reverse <exp>))
trial.sml:1.6-4.35 Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch]
expression: 'Z -> _
result type: 'Y list
in declaration:
reverse = (fn arg => (fn <pat> => <exp>))
However if I change the signature to be fun reverse(x: 'a list, y: 'a list)
then it works why is this so? Is there a way to write this so that I dont need to have to write the type 'a list ?
x y differs from (x,y).
In the first line of the definition
fun reverse x y =
You seem to be trying to write a curried function of type
fn: a' list -> a' list -> 'a list
but in the recursive call
reverse(xs, x::y)
you are treating reverse as if it were and uncurried function of type
fn: a' list * a' list -> 'a list
The problem has nothing at all to do with whether or not you add a type annotation but instead it has to do with where you put parenthesis (and commas, if any). There are two valid fixes depending on what you want the type to be. Since this seems to be homework (there is no obvious non-homework reason to avoid the built-in rev), I'll leave the details to you.
I tried the following:
fun consAll (nil, n) = [n]
| consAll ((x::xs), n) = [[n::x], [consAll(xs, n)]];
But it returns this error:
"Error: operator and operand don't agree [circularity]
operator domain: 'Z list list * 'Z list list list
operand: 'Z list list * 'Z list list
in expression:
((n :: x) :: nil) :: (consAll (,) :: nil) :: nil"
Please tell me where I am going wrong, I am a beginner to SML.
The SML list construction syntax isn't completely straightforward. Since we use [...] to construct literal lists, it is tempting to think we could also use that notation to destructure lists and cons elements onto the head of lists (as we can, for instance, in Prolog). But the cons operator, ::, is just an infix value constructor which takes an item of type 'a and a list of type 'a list and returns a new list with the item consed onto the head of the list. If you then enclose the result of evaluating this construction in square brackets, you have now wrapped the resulting list in another list:
- val xs = [2,3,4];
val xs = [2,3,4] : int list
- [1::xs];
val it = [[1,2,3,4]] : int list list
There are two additional errors in your definition:
First, you are creating a two-item list when you mean to be consing a head onto the tail of a list in the body of your second guarded function definition.
Second, your base is incorrect:
fun consAll (nil, n) = [n]
This says, if the list of lists is empty, then return a singleton list containing the item to be consed onto every list. But you are defining a function which should cons n onto the head of every list which is a member of the list in the first argument. When you run out of lists in the first argument, you should just return an empty list, because there are not more lists to be consed on to. Make sense?
Here are two ways of writing the function you describe. One using simple recursion, the other using the higher-order function List.map:
fun consAll ([], _) = []
| consAll ((x::xs), n) = (n::x) :: consAll(xs, n)
fun mapCons x lists = List.map (fn ls => x :: ls) lists
[[n::x], [consAll(xs, n)]] creates a two-element list but you want to prepend to a list. Use (n :: x) :: consAll(xs, n) instead.
I have a question about mapping lists in ML the problem seems to repeat itself, I have the current datatypes defined :
datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq);
datatype 'a generic_list = List of 'a list
|Seq of 'a seq;
i'm now trying to write the following function that's supposed to recieve a "'a generic_list" and return a "int generic_list:
val rec generic_map = fn (f,List(lst)) => if lst=nil then List([])
else List(f(List.hd(lst))::generic_map(f,List( List.drop(lst,1))));
That code doesn't compile with the error of : right-hand-side of clause doesn't agree with function result type [tycon mismatch] expression:
'Z generic_list
result type: 'Z list
in declaration:
generic_map =
(fn (f,List lst) =>
if lst = nil
then List nil
else List
(f (List.hd lst) ::
generic_map (f,List (List.drop (lst,1)))))
I would like to know whats the problem here and how I can fix it so it will compile, I cant find the mistake
In the 'else' part, you do something :: generic_map (...), which implies that generic_map would have to return a list instead of a generic_list.
Also, I don't see where you handle the seq case at all.
As a general note, I strongly suggest using pattern matching instead of if, List.hd and friends. In particular a comparison like lst = nil is always wrong, since it restricts the list to elements with equality type -- use pattern matching, or at least the List.null predicate.