Static Typing Where Possible, Dynamic Typing When Needed (2011) [pdf]
Since that paper, there's been some convergence in this area. Explicitly typed function parameters and inferred local variables now seem to be the way things are going. Go and Rust take that route. It's been retrofitted to C++, with "auto". Python seems to be headed that way, although the proposal is to have type declarations that are not checked for backwards compatibility reasons, which creates its own problems.
Everything is good and fine, but you cannot have good type inference with dynamic typing and optional static typing. You cannot infer anything about untyped variables, only constants and functions/ops with return types. You can in stricter languages, where the type of a variable is not allowed to change, but in a dynamic languages type inference is limited to explicitly typed variables. You can infer a bit with function signatures and esp. with return types of your internal ops, but everything ends with your normal variable.
I also miss the argument that optional static types enable a proper FFI syntax.
Two answers to "language ceremony":
- smarter humans (keeping a map of type interactions in mind, unit testing type interactions);
- smarter computers (local/global inference and static analysis).
I bet on computers.