It is not possible to perform this concatenation in constant time, simply because the invariance of the data structure does not allow it.
You might think that you can do something similar to the cons ( : operator, which adds an additional element x0 to the front of the list oldList=[x1,x2,x3] (resulting in newList=(x0:oldLIst) ), without need to run the entire list. But this is because you are not touching the existing oldList list, but simply referring to it.
x0 : ( x1 : ( x2 : ( x3 : [] ) ) ) ^ ^ newList oldList
But in your case ( a ++ b ) we are talking about updating the link within the data structure. You want to replace [] with 1:(2:(3:[])) (explicit view [1,2,3] ) with a new tail b . Just count the parenthesis and you will see that we need to go inside to get to [] . This is always expensive because we have to duplicate the entire exterior to make sure that a remains unchanged. In the resulting list, where would old a point to have an unmodified list?
1 : ( 2 : ( 3 : b ) ) ^ ^ a++bb
This is not possible in the same data structure. So, we need the second:
1 : ( 2 : ( 3 : [] ) ) ^ a
And this means duplication of these nodes : that the mentioned linear time is necessarily in the first list. So the “copy (fork)” you were talking about is different from what you said, not in O (1).
make a copy of (fork) a, let's call it a ', there may be some tricks for this in O (1) time
When you talk about a “trick” to develop something at a constant time, you probably think that you are not actually making a full copy, but creating a link to the original a , and the changes are saved as “annotations” (for example , hint: "tail change: use b instead of [] ").
But the fact that Haskell, thanks to his laziness, is all the same! It does not immediately execute the O (n) algorithm, but simply “remembers” that you need a concatenated list until you get access to its elements. But this does not save you from paying the cost at the end. Because, although at the beginning the link was cheap (in O (1), just as you wanted), when you access the actual elements of the list, each instance of the ++ operator adds a bit of overhead (the cost of “interpreting the“ annotation ”that you added to their link), to the access of each element in the first part of the concatenation, actually adding the cost of O (n) finally.