>
Your type signatures have absolutely no assurances with regards to side effects. [example]
This might be true for Swift (which I suspect it is), but it's not true in the wider ecosystem of statically typed languages. A language with a type system with allows controlling side effects, such as Haskell, does indeed make such assurances. In Haskell, for a function to make network calls or output to disk, it must live within the IO monad (which can be seen by its type!). I'm discussing Haskell here because I'm more familiar with it, but there are alternative mechanisms in other languages.
Compare:
f :: [a] -> a -- I promise you this function doesn't do any I/O
with
f :: [a] -> IO a -- this function may do I/O
This is a powerful assurance right there! Of course, Haskell programs as a whole must live in the IO monad (a program without any kind of I/O is useless). But you're encouraged to write as much as the program as you can as pure functions, which can then be tested (unit tested or whatever you prefer) with the very useful knowledge that they cannot do I/O.
Next, generics systems. Languages with OOP and generics, such as Scala and, I suspect, Swift, let you do all sorts of naughty things within generic functions.
But doing generic programming like in Haskell is way safer in this regard. No, you are not allowed specialize the type in f :: [a] -> a. Doing so would be unsafe.
So let's go back to my claim: the above function cannot do anything else but produce a value out of the list I passed it. It doesn't know how to produce something else out of thin air. It cannot "inspect" the value of its type; it has no unsafe "instanceOf" operator. It cannot even apply any operation to the values of the list (except of course list operations), since I didn't declare the generic type had any. This is a very powerful assurance that a dynamic language cannot make, and one that simplifies the tests I have to write.
Because of this property, you are encouraged to write, whenever possible, functions that are as generic as possible. Sometimes you can't, but then you'll specify as little as possible, such as:
sumAll :: Num a => [a] -> a -- "a" is a number with operations +, -, etc.
And then you'll have additional operations available for your type
a, but not as many as if you were writing this with a dynamically typed language with no assurances at all!
Once you realize this, you'll start thinking of generic programming as a tool that constrains the kind of errors you can make (because you have less choices to make, so to speak). And this has a huge impact on testing!
> So in the contrived example of you doing something completely different with my list of ints in the dynamic version is completely possible in many statically typed languages too.
Of course, many statically typed languages are no better than dynamic languages in this regard. I was talking about decent type systems. Not sure where you'll place Swift, though.
Even with languages which allow instanceOf checks, such as Java and I'm willing to bet most OO languages, the practice is frowned upon and wouldn't pass a code review. Unless, of course, there was no other way to solve the problem, but this really would limit the usefulness of generic programming.