0

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.

pad
  • 41,040
  • 7
  • 92
  • 166
mazlor
  • 1,795
  • 4
  • 19
  • 20

2 Answers2

3

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.

Faiz
  • 16,025
  • 5
  • 48
  • 37
1

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) *)
pad
  • 41,040
  • 7
  • 92
  • 166
  • maybe the last line has to be val yz = (#2 xyz, #3 xyz) (* (8, 9) *) instead of val yz = (#2 xys, #3 xyz) (* (8, 9) *). – mazlor Jan 18 '13 at 19:15