The most obvious thing an optional type system can't do is perform implicit searches that affect the semantics of the resulting program. Many of us consider this to be a good thing!
> what are you missing out on by not having pervasive static typing built in like in Haskell
One example of an implicit search is Haskell's return type polymorphism, where overloading is resolved by solving a logic puzzle to find a function with an unambiguous type signature match.
You can get around this problem statically or dynamically:
Statically #1: Explicit Search
Although probably overkill for something like resolving "read" to "readInteger" vs "readFloat", you can invoke a logic engine on a piece of code. This means that your language needs to support staging constructs, so that you can run code at various "times". Macros give us this, but there are potentially better mechanisms. You'll also need first class representations of previous loaded code, code under observation, the type solver, etc. People do things like this with core.logic in Clojure now: They generate a data structure, solve something about it, then either generate code from the solution or interpret some annotated data structure.
Statically #2: Explicit Parameterization
Just be explicit and write readFloat instead of read! If you need your code to be generic, you can use macros etc to fill in that slot at compile time.
Dynamically: OOP Techniques
See "Existential Types". As long as your coding to interfaces instead of concrete types, you can create proxy objects who can perform dynamic dispatch. What is a search problem at compile time is often a key lookup at runtime. This is what happens behind the scenes with generic monad code in GHC and friends anyway: They reify a dictionary of methods and thread that through.