If String and None are both valid return types for a function, then you tag that so the inference system knows it's a valid solution
In other words, I need to do extra work to tell the type system about the function's return type.
If you know they share functionality, you can declare that they share functionality (any language with type classes)
Same comment here; the language is making me do extra work that I don't have to do in a dynamically typed language. So there's a tradeoff.
This sounds tedious right up until the first time you write a complicated program, get it to compile, and it just runs correctly the very first time.
See, here's the thing: I often write non-trivial programs in Python that run correctly the very first time. So I've had this same experience in a dynamically typed language.
Perhaps a language like Haskell would raise the bar, so to speak, for how complex a program can get and still allow this to happen; but even then, it would only matter if you were writing the particular kind of program that can benefit from the difference.
Lastly, in a language like Haskell, type declarations are almost always optional.
It's the "almost always" that's key here; basically, what the rest of your discussion here shows is that the type declarations end up not being optional in precisely the cases where, in a dynamically typed language, you would just be making changes and running code, not spending extra work on adding type declarations to resolve ambiguities for the compiler. So there's a tradeoff.
As I've said in several comments in this thread, I'm not saying dynamic typing is always better; I'm saying it depends on the kind of application you're writing. There are tradeoffs involved, and they don't always end up favoring static typing.
In other words, I need to do extra work to tell the type system about the function's return type.
If you know they share functionality, you can declare that they share functionality (any language with type classes)
Same comment here; the language is making me do extra work that I don't have to do in a dynamically typed language. So there's a tradeoff.
This sounds tedious right up until the first time you write a complicated program, get it to compile, and it just runs correctly the very first time.
See, here's the thing: I often write non-trivial programs in Python that run correctly the very first time. So I've had this same experience in a dynamically typed language.
Perhaps a language like Haskell would raise the bar, so to speak, for how complex a program can get and still allow this to happen; but even then, it would only matter if you were writing the particular kind of program that can benefit from the difference.
Lastly, in a language like Haskell, type declarations are almost always optional.
It's the "almost always" that's key here; basically, what the rest of your discussion here shows is that the type declarations end up not being optional in precisely the cases where, in a dynamically typed language, you would just be making changes and running code, not spending extra work on adding type declarations to resolve ambiguities for the compiler. So there's a tradeoff.
As I've said in several comments in this thread, I'm not saying dynamic typing is always better; I'm saying it depends on the kind of application you're writing. There are tradeoffs involved, and they don't always end up favoring static typing.