Instructor: Using the keyword let, we can bind the string value hello to the name greeting. Once we do that, we can use this name reference, for example, by just typing greeting. It results in the output as we would type the string hello itself.
We can use greeting now and pass it around as it would provide the string, for example, to the function print_endline. Of course, let bindings aren't limited to strings. We can bind any value of any type to any name. For example, here, we bind 32 to the name age.
Let bindings in Reason are similar to variable declarations in other languages. Since variable has different meanings in various languages, in this course, I'm only going to use the terms let binding and name.
The general pattern of let binding is that it starts with the keyword let, followed by the desired name, which must start with a lowercase character, or an underscore. Then, comes a colon followed by the type. Right after that, the equal sign, and then the expression to be bound.
So far, we only have seen how to declare the type of a value, but what did I mean by can be inferred? Let me explain by an example. We bind the string value, Jim, to the name Jim, but this time, we leave out the type definition.
As you can see, the reason compiler inferred that the type of the value is a string. This is a really great feature since it allows us to have full type safety without declaring the types all the time. Of course, this works for all sorts of types. Let's bind the value 200 to height. It also works for float, or even a list of strings.
In conclusion, types are optional, but also can explicitly be written down by choice. This goes so far that theoretically, your whole program could be type-inferred. In reality, though, in some cases, the compiler needs your assistance with the type declaration. Sometimes, it's also useful to be more explicit.
Let's move on and talk about immutability. Let bindings are immutable. This means if we bind the value Vienna to the name city, we can't change the value of that let binding. That said, we could create a new let binding of the same name, which shadows the previous binding. From that point onward, the let binding will refer to the newly-assigned value.
We could even use a different type since when shadowing a let binding, it has nothing to do anymore with the previous one. Next up, I want to demonstrate you how to create a type alias.
We start with the type keyword followed by the desired name. Right after that, comes an equal sign, and then the type definition to be aliased. Now, we can use score instead of our integer. Even type inference works, meaning in this case, the compiler will even opt-in to show the type alias score instead of the type integer.
Of course, type aliases are not limited to basic types like integer. We can create a type alias score, which is a list of the type score. We can even use shadowing for type declarations, meaning, if we declare type alias foo for integer, then use it, re-declare it to string, and use foo again, it works perfectly fine.
By the way, declaring the types isn't necessary here. I only did it to clearly demonstrate shadowing of type declarations. Here, the same code snippets leveraging type inference.
That said, I recommend to keep shadowing of bindings, and especially types, to a minimal, and use this feature with caution to avoid confusion.