Many modern languages support type inference to some degree. The special thing about ReasonML is that the type inference is much more robust and complete.
In practice you don't need to provide any types annotations at all at any point in your program and you should still get the same safety benefits.
Every value in your code will automatically have one single type assigned to it based on how it is being used. When the compiler finds contradictions it will let you know about it. On the other hand, Typescript will try to unify the type to some sort of Any, which is probably not what you want.
One problem with people selling ReasonML's type inference is that it's not depicting the actual developer experience.
The first thing to note is that you do interact with types to build a mental model! The editor will show them to you as you move your cursor around the code. It will do that for every single value. Just move the cursor over that `requestContext` argument and voilà, it'll show you the type.
Another important aspect is that you still have to define types. Have a person object with a bunch of fields? You do have to type annotate every single field. Have multiple actions to handle in reducers? You need to tell ReasonML in advance what those are using a variant type.
And finally, if you care about composition/abstraction you should provide interfaces for modules. This requires writing types for all functions explicitly to ensure that you are exposing a correct protocol. This is optional, but it helps both the author of the code and the consumers.
Type-inference is just a nice to have feature for the low-level implementation details.
Nothing stops you using type annotations in ML, and good developers will do so where it is useful. But it doesn't require them in cases where they are redundant, for instance when you have a parameter `foo` of type `Foo`.
Why do people care about this? Writing type annotations is not that much work, but they make your code more readable.
I can see the argument for it when you have unspellable nested generic types, or when you don't want to write the same thing on both sides of an assignment. Simple type inference like in TS gives you that.
Having really smart type inference makes your compiler slower and more complicated. What's the real-world benefit?
> Typescript will try to unify the type to some sort of Any, which is probably not what you want.
Not really, unless you push the type inference beyond its limits, in which case you should just change your code.
> Why do people care about this? Writing type annotations is not that much work (...)
I personally care about this when I'm prototyping something. Not having to write types means that I can simply write what's on my mind. The benefit of good type inference is that the compiler can still help me highlight any mistakes I made during this process.
Another benefit is that some production code can get very complex. Having to type every single value is just too cumbersome and distracting. It contributes to boilerplate and increases cognitive load, in my opinion.
> Having really smart type inference makes your compiler slower and more complicated.
I would actually disagree with this. First of all, ReasonML's compiler is absurdly fast. No, seriously try it. Sometimes I go and double check the generated JS code just to be sure it actually did anything.
Regarding the "more complicated" part – the only reason why full type inference works is because it is based on a very solid theoretical foundation. It might be somewhat "complicated", but it will never be as complicated as something ad-hoc that needs to account for all inconsistencies that exist in untyped languages like JavaScript.
> It contributes to boilerplate and increases cognitive load, in my opinion.
In my experience with OCaml, I found that knowing the types of my variables reduce the cognitive load of trying to infer the types myself when reading the code, so despite OCaml supporting type inference, I use explicit type annotations almost everywhere.
Being a Vim/Merlin user I normally just type `<Leader>t` to see the type of the value under the cursor (this maps to `:MerlinTypeOf` if I recall correctly).
What I do use type annotations for is debugging. If the compiler finds a type error, in some situations it helps adding type annotations to find where the actual error is.
If you are writing in a Functional paradigm you'll most likely create a lot of small functions that perform very direct actions and then chain them all together to get a more robust operation. When I write Haskell I'll often annotate the larger functions and keep all the smaller ones to auto-generate. They will either determine the type based on the larger function's definition or by operations inside the smaller ones. No point in me writing an extra line for a one line function when its obvious the type.
In practice you don't need to provide any types annotations at all at any point in your program and you should still get the same safety benefits.
Every value in your code will automatically have one single type assigned to it based on how it is being used. When the compiler finds contradictions it will let you know about it. On the other hand, Typescript will try to unify the type to some sort of Any, which is probably not what you want.