Search code examples
sml

SML How to prepend an element to the beginning of every list in a list of lists?


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.


Solution

  • 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