A short introduction to ReasonML types and functor types from a beginner. I started to learn some functional patterns after reading ’s amazing book . This book made me crazy about this new paradigm, that I never learned, and made me want to practice these patterns somewhere. Brian Lonsdorf “Mostly Adequate Guide to Functional Programming in JavaScript” I found myself using it in my fun projects but never in my real-work projects — it always fell in the “most javascript programmers don’t code like this” territory. My colleagues were right, so I decided to learn a different language. Originally, it was Haskell. I started to learn it, but it was too theoretical (or “academic”) for me: I’m a very practical guy, and I like to be able to use and build software with the paradigms and languages I learn first. So… for the last couple of days I’ve been learning . Reason Reason? Reason, or , is a new syntax of OCaml. It looks more like JavaScript so it is easier to get start with. And the OCaml part doesn’t need to tell you much, other than that you have a big community behind you, in a language that is here for years. OCaml has as their package management tool, and the community is active. Check out to see some cool stuff from the OCaml community. ReasonML opam awesome-ocaml What does “a new syntax” mean? Having a new syntax for OCaml, is like using CoffeeScript or transpiling newer JS syntax to older JS syntax using Babel — writing in one syntax, then compiling it to another syntax. Reason “compiles” to OCaml, and along with BuckleScript, a tool that compiles OCaml to very efficient JavaScript, you can see the chain: Reason ➡ OCaml ➡ JavaScript. Both OCaml and Reason have a fully sound (and inferred) type system, so 100% of your code is covered with types on compile time. That means less bugs. The inference part is amazing too; it means that you don’t have to write the types yourself. The compiler understands your code, and does that for you. For developers coming with Java-like type systems, to type systems like Reason has — note that in Reason, s aren’t a thing. You can’t send as an argument for a function which wants to get a . It’s just not possible. , it is just that you have a special way of doing so. That way is called . null null string It’s not that the whole concept of absence isn’t baked into Reason option The option type In Reason, you can declare types as constructors. Constructors have data in them, but it is not mandatory. Let’s take the type for instance, its declaration looks like the following: can option type option('a) = None | Some('a) Woohoo, there’s a lot going on here for first comers. Let’s go over this definition together. First, we declare a , called . Then, there are the type parameters: that thing is called “type parameter” — it is used like Generics or Templates in languages like Java, C++, etc. This is so can “wrap” many different types: you can have if you have a nullable integer, or if you have a nullable string! Type parameters are always prefixed with a tick ( ), so you can’t unsee it in examples. You can have more than one type parameter — more on that later. type option 'a option option(int) option(string) ' Then, we say that our type is a union of 2 constructors: , which receives nothing as data, and , which receives data with the type , as the type parameter says. None Some 'a The built-in type is the way we handle absence in Reason. In Haskell, it is called . We always know that we receive an option type, and when we have an option type, we always have to unpack it before we use it. We unpack data with a cool feature called : option Maybe pattern matching let greet = (optionalName: option(string)) : string =>switch optionalName {| Some(name) => "Hello, " ++ name| None => "Who are you?"}; print_endline(greet(Some("Gal"))); /* prints "Hello, Gal" */print_endline(greet(None)); /* prints "Who are you?" */ See how I unpack the data with ease. When I get , I return a string that contains . When I get with some in it, I return . It may look weird that it actually works like that, but it does! ( ) None Who are you? Some name Hello, ${name} you can click here to see what it compiles to in the Reason Try page Okay okay… an optional type. What is a functor? Now that we know what is the type, we can continue from here and use it as our example for the rest of the article. option A functor is a data type that can be mapped. That means that you can call the function on it. The function takes a functor and a function, then applies the provided function to the functor’s value (unwraps it), and wraps the result with the an instance of the functor again. map map You may already be familiar with : . This is because that has multiple values in it. The provided function is evaluated with every value in the array, and in return, you get a new Array! By the way, in Reason, you don’t call methods on a value — that’s OOP. The way of invoking methods is to invoke a function and pass the value to it: [Array#map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) from ES5 array.map(fn) Array is a functor let increment = x => x + 1;map(Array[1], increment) // => Array[2] The type, Just like , is a functor too. To understand how it would work let’s split it to its two constructors: and . option Array Some None What would happen if we over ? I’d suggest that it should have the same behavior as mapping over an empty array: it should do nothing, it shouldn’t execute the provided function at all —it should just return . For , I’d suggest that it should have the same behavior as mapping over an array with a single value. So the single value will be applied to the function, then be packed again with : map None None Some Some let increment = x => x + 1;map(None, increment); // => Nonemap(Some(1), increment); // => Some(2) Implementing this function is just a couple lines of code: map let mapOption = (opt, fn) =>switch opt {| None => None| Some(value) => Some(fn(value))}; Reason’s amazing type system ensures that is a function that receives the provided value, and returns a new option. The full type declaration for is: fn option mapOption let mapOption = (opt: option('a), fn: 'a => 'b) : option('b) Let’s go over it together: we first declare a function called mapOption that receives an argument called , with the type of . As we already talked about, is a type parameter, making this function get then, we receive a function that receives (the type parameter we had in the argument), and returns , which is or isn’t a different type. Then, the function returns , which is the same type as the inner function result type, but wrapped in an . opt option(‘a) ‘a any type of option. ‘a opt ‘b option(‘b) option In other words, we receive an , then we receive a function that transforms a value from type to , and then we return : we support transforming between the inner types, while we’re still in land: option(‘a) ‘a ‘b option(‘b) option let stringify = x => string_of_int(x); // int => stringmapOption(Some(1), stringify); // Some("1")mapOption(None, stringify); // None More functor examples We can declare another type — the functor. The functor will be almost like , but its negative side will have a value too. You can treat its value as a , or , or just . When you return the functor, you’re basically saying that “this method may fail”. You can use in validations and for database queries, or for anything whose error is important: result result option reason error failure result result type result('s, 'f) = Success('s) | Failure('f) The behavior is almost identical to the behavior. If we have , we will take its data and apply it to the provided function. If we have a , we wouldn’t apply the value to the function, and just leave that as it is. The implementation, again, is a couple lines of code: map optionMap Success Failure Failure let mapResult = (res, fn) =>switch res {| Failure(failure) => Failure(failure)| Success(success) => Success(fn(success))}; This data structure helps us build our program as a tree. When we have a problem, we stop applying our logic to it. We basically stop going through the “happy path” until we handle the failure. When we map over _Failure_ , we stay with the same failure we had, so no data transformation will happen once we have a _Failure_ . In the next article, I’m going to write about Monads, a special case of , that can help you a lot while building data transformations. Functors Up until then, what are your ideas of custom functors? Thanks for reading!