The first solution is infinitely better than the second solution. In Haskell, tail recursion is not the main concern. Rather, the primary consideration is laziness. Furthermore, operations like list reversal (
myReverse, which you haven't shown us) and list concatenation (the
++ operator) should be avoided wherever possible, since they involve traversing an entire list to its end. Considering that lists in Haskell may be infinitely long, you could be inviting trouble.
I consider your
insertAt v  n = [v] case to be superfluous and harmful. If you are inserting an element into an empty array, I think it's fair to require the index to be
1. I would write it like this:
insertAt :: a -> [a] -> Int -> [a]
insertAt v xs 1 = v : xs
insertAt v (x:xs) n = x : (insertAt v xs (n - 1))
In my opinion, the interface specified in the challenge is weird: the function would be better named
insertBefore, since I expect that inserting an element at position 0 would place it at the head of the resulting list.
As a rule of thumb, Haskell functions should be designed so that the parameters are ordered starting from the one that is least likely to vary. I would certainly not place the index at the end. One better design would be
insertAt value index list, such that
insertAt value index could be treated as a partial function to be applied to some list. Another reasonable design would be
insertAt list index value, such that
insertAt list index could be treated as a partial function that is waiting to fill a specified hole in the
list. However, with the current specification, the partial function
insertAt value list means "add
list, but we don't know where yet", which seems like a rare use case to me.
insertAt item xs n = take n xs ++ [item] ++ drop n xs\$\endgroup\$
splitAtwould get both in one pass. \$\endgroup\$