listprologdeclarativecons

Prolog lists: combination of comma and |


In Prolog, [a, b, c] = [a, b, c | []]

However, I am confused about what the head of [a, b, c | []] is? If [a, b, c] = [a, b, c | []] why is it not true that: [a, b, c] = [a, b, c | []] = [[a, b, c | []] | []]] = ... (so I added another empty list at the end of the head, since they are elements separated by a comma, just like it is the case with [a, b, c] where we also added an empty list)

So I see a comma as: combine all the elements via the | operator, and add an empty list at the end. "a, b, c" (the head of [a, b, c | []]) would then become [a, b, c | []] again and so on.

But there is something wrong in my interpretation, since what I just said is wrong.

And If I'm not mistaken, | has a higher precedence than the comma operator. So [A, B|C] = [A|[B|C]]. But why is it not the case that [A, B|C] = [A|[[B|C]|[]]].

I can memorize the correct definition as follows: combine the elements via |, and add an empty list at the end only if the last operand of the commas is enclosed by a ]. So then: [a, b, c] = [a, b, c | []], and if I got [a, b, c | []], even though I got commas here, I wouldn't add an empty list since the last element (c) is not enclosed by a ]. But this seems to be not a good way to understand this.


Solution

  • Take a step back; [] makes a list, [[]] makes a list inside a list.

    ?- write_canonical([a,b,c|[]]).
    .(a,.(b,.(c,[]))).
    
    ?- write_canonical([[a,b,c|[]]|[]]).
    .(.(a,.(b,.(c,[]))),[]).
    

    They can't = each other because they are different structure/shapes. That's it, really.


    I am confused about what the head of [a, b, c | []] is?

    The head of a list is the first item, that's a list of three items and the first one is a:

    ?- [Head|Tail] = [a,b,c|[]].
    Head = a,
    Tail = [b, c].
    

    why is it not true that: [a, b, c | []] = [[a, b, c | []] | []]]

    As Evgeny answered, [[a, b, c | []] | []] is a one-item list with a three-item list inside: [ List | []], which is also known as a nested list (a list inside a list).

    So I see a comma as: combine all the elements via the | operator, and add an empty list at the end. "a, b, c" (the head of [a, b, c | []]) would then become [a, b, c | []] again and so on.

    I see a [] as making a list, comma to separate the items in the list, pipe to chain another list on at the end. Put an empty list on the end to indicate nothing else the end, but there's no need to write that because just [a,b,c] is clear enough for that.


    Edit to reply to comments:

    1: Sometimes the comma operator adds an empty list to the end, sometimes it doesn’t.

    I just took the last comma and added an empty list together with a pipe

    I don't understand this focus on the comma; you added a pipe and an empty list - and you wrapped the whole thing in another layer of list. You're writing [[[[[[list inside list]]]]]]]] but talking as if you're writing [] [] [] [] [] list next to list.

    You aren't chaining another empty list onto the end like a b c -> a b c [] -> a b c [] []. Instead you are a b c -> (a b c []) -> ((a b c []) []) which is different.

    [a,b,c] is a list. Then [a,b,c|Rest] is a list with an unground tail ("a list must terminate with an empty list" is false, this list doesn't have a fixed length). Then [a,b,c|Rest], Rest = [1,2,3] chains the numbers to the end of the letters. Then [a,b,c|Rest], Rest=[] chains empty list on, and that is the way Prolog knows a list has ended, so it's now a list of fixed length 3. It's valid to write [a,b,c|[]] if you want to, but I don't know why you would want to since it's the same as [a,b,c] and that's shorter and easier to write.

    It's not valid to write [a,b,c|[]|[]] and you're right [a|b] is not a valid list. I still see the outer [ ] as the list maker more than your focus on the comma, is why I said that.

    When is the empty list added and when not?

    When you want it. It would be tedious to write it out all the time, and you don't have to.

    For my first question, it may be helpful to mention that my question comes from the fact that we can replace comma by a pipe

    No?

    but SOMETIMES we add an empty list which was hidden.

    If you want. It's the end of a fixed-length list. It needs to be there behind the scenes, you don't always have to call attention to it. [a,b,c] will have it. And it's not the same as a list with an empty list as the last item so casually speaking it "ends with an empty list" which would be [a,b,c,[]].