> I can promise you my function doesn't write to disk, doesn't output to the screen, etc. You cannot promise the same with your function.
WHAT?!
Your type signatures have absolutely no assurances with regards to side effects. They cannot even make a claim that the function is thread safe, let alone that it doesn't write to disk our output to the screen.
I'm baffled at why you think that is true:
int foo(int bar):
// network call here
// write a log to disk here
// change a global value here
return happy_int
And you are woefully mistaken about about this claim as well: "it must work with the list I passed it, because it doesn't know anything else."
Many languages that actually have good generic type systems allow for type specialization. That means that I can provide different implementations for different types. 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.
> 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.
WHAT?!
Your type signatures have absolutely no assurances with regards to side effects. They cannot even make a claim that the function is thread safe, let alone that it doesn't write to disk our output to the screen.
I'm baffled at why you think that is true:
And you are woefully mistaken about about this claim as well: "it must work with the list I passed it, because it doesn't know anything else."Many languages that actually have good generic type systems allow for type specialization. That means that I can provide different implementations for different types. 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.