Lets look at your first example:
let f = map tail.tail.lines
f "fasdf\nfasdfdsfd\n"
Firstly, lines breaks your input string into an array of strings: ["fasdf","fasdfdsfd"].
Now, working right to left, tail drops the "head" of the list: ["fasdfdsfd"]
Lastly, map tail applies the "tail" to each element in the list: ["asdfdsfd"]
Your second example works similarly:
let f = map (tail.tail).lines
f "fasdf\nfasdfdsfd\n"
Again, you break the input string into an array of strings: ["fasdf","fasdfdsfd"]
Now however, you're creating a composite function (tail.tail) (drop the "head" of the list, twice), and mapping it to each element in the list.
Therefore you drop the first two characters of each string.
["sdf","sdfdsfd"]
Both your examples are working as intended. Have a read about associativity and composite functions in haskell to understand more about it.
Edit: the difference is essentially this:
map tail (tail lines) vs map (tail.tail) lines
Remember that in Haskell, functions are first class citizens - you can create composite functions (example: (+1).(+1)) and do other operations (such as map a function to a list) that are not common in other languages.