Search code examples
listsmlsmlnj

What does (l+v) :: ...(list + integer :: (cons operand) ..) mean in SML?


- fun addto (l,v) =
= if null l then nil
= else hd l + v :: addto (tl l,v);
val addto = fn : int list * int -> int list

addto ([1,2,3],2);
val it = [3,4,5] : int list
- addto ([1,2,3],~2);
val it = [~1,0,1] : int list

This is an SML function in my slides. I don't get how (l+v) can work here. But it works actually:

addto ([1,2,3],2);
val it = [3,4,5] : int list

I am thinking it goes something like that:

   addto([1,2,3],2);  
   addto([2,3], 2);  
   addto([3], 2);   
   addto([],2)

now it's actually l nill so it returns it to addto([3], 2);
But what does hd l + v :: addto (tl l,v); mean anyway; I thought that the "cons" operator :: has to be defined something like : <list_object_of_a_certain_type> ::
And here my pseudo-name <list_object_of_a_certain_type> is actually integers in the example with addto([1,2,3],2).
But inside my function we have the phrase (l+v :: ..) while l is a list and v is an int so what is l+v?

p.s I am a complete beginner so forgive me if it all too simple


Solution

  • First :: is defined as:

    datatype 'a list = nil | :: of 'a * 'a list
    

    So, for instance, 1 :: [2, 3] is the list [1, 2, 3].

    Then in your code, your expression is interpreted as:

    ((hd l) + v)) :: (addto (tl l,v))
    

    So basically, your function can be rewritten as follows:

    fun addto (l,v) =
    if null l then nil  (*  if l is empty the result is the empty list  *)
    else let
      val head = hd l  (*  take the first element of list l  *)
      val sum = head + v  (*  then add it v  *)
      val result_tail = addto (tl l,v)  (*  and compute x + v for all x in the tail of my list  *)
    in
       sum :: result_tail  (*  the result is the sum I just computed followed by what the recursive call gave me  *)
    end
    

    Finally, note that in most cases, you don't need functions such as null or head because we write functions with pattern matching construction. This greatly enhances readability and it is often the case that it suppresses the need for such functions. For instance, addto can be rewritten more simply as:

    fun addto ([], _) = []
      | addto (x :: tl, v) = (x + v) :: addto (tl, v)
    

    Simpler isn't it?