Well, let's take a look at the type signature for the curried function foldr:
>:t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
So foldr takes a binary function (i.e. a->b->b), a b value, a list of a values, and returns a b value.
Let's also look at the documentation for foldr to get a more clear definition:
foldr, applied to a binary operator, a starting value (typically the
right-identity of the operator), and a list, reduces the list using
the binary operator, from right to left:
Now, let's take a look at the type signature for myConcat xs = foldr (++) []
> :t myConcat
myConcat :: t -> [[a]] -> [a]
Hmm...that's not what we wanted...
The problem is that you never provided foldr a value of type [a]. So now, myConcat needs some value, of any type, to satisfy xs and a value of type [a] to complete foldr (++) [], like:
> myConcat 2 [[1,2],[3,4]]
[1,2,3,4]
> myConcat Nothing [[1,2],[3,4]]
[1,2,3,4]
That works, but the first argument is just a waste.
However, if we pass that xs value over to foldr (++) [], like:
myConcat xs = foldr (++) [] xs
and check its type signature
> :t myConcat
myConcat :: [[a]] -> [a]
Ah, much better. Now myConcat uses xs to complete the foldr function.
Also, myConcat = foldr (++) [] also works, and is actually an example of point-free style programming. If we check the type signature of foldr (++) [],
> :t foldr (++) []
foldr (++) [] :: [[a]] -> [a]
Since we already provided foldr its first two arguments through partial application, we get a function back that will takes a [[a]] value and do what we want! So we just assign it to a name, and it works just like the example above, but we didn't need to explicitly pass arguments!
> let myConcat = foldr (++) []
> :t myConcat
myConcat :: [[a]] -> [a]
> myConcat [[1,2],[3,4]]
[1,2,3,4]