Reason is a syntax over OCaml that is easier to get started for programmers who are familiar with C-style syntax like JavaScript. BuckleScript is part of the toolchain which compiles Reason to JavaScript so you can write statically typed code for anywhere that JavaScript runs.
1/* Comments start with slash-star, and end with star-slash */
2
3/*----------------------------------------------
4 * Variable and function declaration
5 *----------------------------------------------
6 * Variables and functions use the let keyword and end with a semi-colon
7 * `let` bindings are immutable
8 */
9
10let x = 5;
11/* - Notice we didn't add a type, Reason will infer x is an int */
12
13/* A function like this, take two arguments and add them together */
14let add = (a, b) => a + b;
15/* - This doesn't need a type annotation either! */
16
17/*----------------------------------------------
18 * Type annotation
19 *----------------------------------------------
20 * Types don't need to be explicitly annotated in most cases but when you need
21 * to, you can add the type after the name
22 */
23
24/* A type can be explicitly written like so */
25let x: int = 5;
26
27/* The add function from before could be explicitly annotated too */
28let add2 = (a: int, b: int): int => a + b;
29
30/* A type can be aliased using the type keyword */
31type companyId = int;
32let myId: companyId = 101;
33
34/* Mutation is not encouraged in Reason but it's there if you need it
35 If you need to mutate a let binding, the value must be wrapped in a `ref()`*/
36let myMutableNumber = ref(120);
37
38/* To access the value (and not the ref container), use `^` */
39let copyOfMyMutableNumber = myMutableNumber^;
40
41/* To assign a new value, use the `:=` operator */
42myMutableNumber := 240;
43
44/*----------------------------------------------
45 * Basic types and operators
46 *----------------------------------------------
47 */
48
49/* > String */
50
51/* Use double quotes for strings */
52let greeting = "Hello world!";
53
54/* A string can span multiple lines */
55let aLongerGreeting = "Look at me,
56I'm a multi-line string
57";
58
59/* A quoted string can be used for string interpolation and special chars
60 Use the `js` annotation for unicode */
61let world = {js|🌍|js};
62
63/* The `j` annotation is used for string interpolation */
64let helloWorld = {j|hello, $world|j};
65
66/* Concatenate strings with ++ */
67let name = "John " ++ "Wayne";
68let emailSubject = "Hi " ++ name ++ ", you're a valued customer";
69
70/* > Char */
71
72/* Use a single character for the char type */
73let lastLetter = 'z';
74/* - Char doesn't support Unicode or UTF-8 */
75
76/* > Boolean */
77
78/* A boolean can be either true or false */
79let isLearning = true;
80
81true && false; /* - : bool = false; Logical and */
82true || true; /* - : bool = true; Logical or */
83!true; /* - : bool = false; Logical not */
84
85/* Greater than `>`, or greater than or equal to `>=` */
86'a' > 'b'; /* - bool : false */
87
88/* Less than `<`, or less than or equal to `<=` */
891 < 5; /* - : bool = true */
90
91/* Structural equal */
92"hello" == "hello"; /* - : bool = true */
93
94/* Referential equal */
95"hello" === "hello"; /* - : bool = false */
96/* - This is false because they are two different "hello" string literals */
97
98/* Structural unequal */
99lastLetter != 'a'; /* -: bool = true */
100
101/* Referential unequal */
102lastLetter !== lastLetter; /* - : bool = false */
103
104/* > Integer */
105/* Perform math operations on integers */
106
1071 + 1; /* - : int = 2 */
10825 - 11; /* - : int = 11 */
1095 * 2 * 3; /* - : int = 30 */
1108 / 2; /* - : int = 4 */
111
112/* > Float */
113/* Operators on floats have a dot after them */
114
1151.1 +. 1.5; /* - : float = 2.6 */
11618.0 -. 24.5; /* - : float = -6.5 */
1172.5 *. 2.0; /* - : float = 5. */
11816.0 /. 4.0; /* - : float = 4. */
119
120/* > Tuple
121 * Tuples have the following attributes
122 - immutable
123 - ordered
124 - fix-sized at creation time
125 - heterogeneous (can contain different types of values)
126 A tuple is 2 or more values */
127
128let teamMember = ("John", 25);
129
130/* Type annotation matches the values */
131let position2d: (float, float) = (9.0, 12.0);
132
133/* Pattern matching is a great tool to retrieve just the values you care about
134 If we only want the y value, let's use `_` to ignore the value */
135let (_, y) = position2d;
136y +. 1.0; /* - : float = 13. */
137
138/* > Record */
139
140/* A record has to have an explicit type */
141type trainJourney = {
142 destination: string,
143 capacity: int,
144 averageSpeed: float,
145};
146
147/* Once the type is declared, Reason can infer it whenever it comes up */
148let firstTrip = {destination: "London", capacity: 45, averageSpeed: 120.0};
149
150/* Access a property using dot notation */
151let maxPassengers = firstTrip.capacity;
152
153/* If you define the record type in a different file, you have to reference the
154 filename, if trainJourney was in a file called Trips.re */
155let secondTrip: Trips.trainJourney = {
156 destination: "Paris",
157 capacity: 50,
158 averageSpeed: 150.0,
159};
160
161/* Records are immutable by default */
162/* But the contents of a record can be copied using the spread operator */
163let newTrip = {...secondTrip, averageSpeed: 120.0};
164
165/* A record property can be mutated explicitly with the `mutable` keyword */
166type breakfastCereal = {
167 name: string,
168 mutable amount: int,
169};
170
171let tastyMuesli = {name: "Tasty Muesli TM", amount: 500};
172
173tastyMuesli.amount = 200;
174/* - tastyMuesli now has an amount of 200 */
175
176/* Punning is used to avoid redundant typing */
177let name = "Just As Good Muesli";
178let justAsGoodMuesli = {name, amount: 500};
179/* - justAsGoodMuesli.name is now "Just As Good Muesli", it's equivalent
180 to { name: name, amount: 500 } */
181
182/* > Variant
183 Mutually exclusive states can be expressed with variants */
184
185type authType =
186 | GitHub
187 | Facebook
188 | Google
189 | Password;
190/* - The constructors must be capitalized like so */
191/* - Like records, variants should be named if declared in a different file */
192
193let userPreferredAuth = GitHub;
194
195/* Variants work great with a switch statement */
196let loginMessage =
197 switch (userPreferredAuth) {
198 | GitHub => "Login with GitHub credentials."
199 | Facebook => "Login with your Facebook account."
200 | Google => "Login with your Google account"
201 | Password => "Login with email and password."
202 };
203
204/* > Option
205 An option can be None or Some('a) where 'a is the type */
206
207let userId = Some(23);
208
209/* A switch handles the two cases */
210let alertMessage =
211 switch (userId) {
212 | Some(id) => "Welcome, your ID is" ++ string_of_int(id)
213 | None => "You don't have an account!"
214 };
215/* - Missing a case, `None` or `Some`, would cause an error */
216
217/* > List
218 * Lists have the following attributes
219 - immutable
220 - ordered
221 - fast at prepending items
222 - fast at splitting
223
224 * Lists in Reason are linked lists
225 */
226
227/* A list is declared with square brackets */
228let userIds = [1, 4, 8];
229
230/* The type can be explicitly set with list('a) where 'a is the type */
231type idList = list(int);
232type attendanceList = list(string);
233
234/* Lists are immutable */
235/* But the contents of a list can be copied using the spread operator */
236let newUserIds = [101, 102, ...userIds];
237
238/* > Array
239 * Arrays have the following attributes
240 - mutable
241 - fast at random access & updates */
242
243/* An array is declared with `[|` and ends with `|]` */
244let languages = [|"Reason", "JavaScript", "OCaml"|];
245
246/*----------------------------------------------
247 * Function
248 *----------------------------------------------
249 */
250
251/* Reason functions use the arrow syntax, the expression is returned */
252let signUpToNewsletter = email => "Thanks for signing up " ++ email;
253
254/* Call a function like this */
255signUpToNewsletter("hello@reason.org");
256
257/* For longer functions, use a block */
258let getEmailPrefs = email => {
259 let message = "Update settings for " ++ email;
260 let prefs = ["Weekly News", "Daily Notifications"];
261
262 (message, prefs);
263};
264/* - the final tuple is implicitly returned */
265
266/* > Labeled Arguments */
267
268/* Arguments can be labeled with the ~ symbol */
269let moveTo = (~x, ~y) => {/* Move to x,y */};
270
271moveTo(~x=7.0, ~y=3.5);
272
273/* Labeled arguments can also have a name used within the function */
274let getMessage = (~message as msg) => "==" ++ msg ++ "==";
275
276getMessage(~message="You have a message!");
277/* - The caller specifies ~message but internally the function can make use */
278
279/* The following function also has explicit types declared */
280let showDialog = (~message: string): unit => {
281 () /* Show the dialog */;
282};
283/* - The return type is `unit`, this is a special type that is equivalent to
284 specifying that this function doesn't return a value
285 the `unit` type can also be represented as `()` */
286
287/* > Currying
288 Functions can be curried and are partially called, allowing for easy reuse */
289
290let div = (denom, numr) => numr / denom;
291let divBySix = div(6);
292let divByTwo = div(2);
293
294div(3, 24); /* - : int = 8 */
295divBySix(128); /* - : int = 21 */
296divByTwo(10); /* - : int = 5 */
297
298/* > Optional Labeled Arguments */
299
300/* Use `=?` syntax for optional labeled arguments */
301let greetPerson = (~name, ~greeting=?, ()) => {
302 switch (greeting) {
303 | Some(greet) => greet ++ " " ++ name
304 | None => "Hi " ++ name
305 };
306};
307/* - The third argument, `unit` or `()` is required because if we omitted it,
308 the function would be curried so greetPerson(~name="Kate") would create
309 a partial function, to fix this we add `unit` when we declare and call it */
310
311/* Call greetPerson without the optional labeled argument */
312greetPerson(~name="Kate", ());
313
314/* Call greetPerson with all arguments */
315greetPerson(~name="Marco", ~greeting="How are you today,");
316
317/* > Pipe */
318/* Functions can be called with the pipeline operator */
319
320/* Use `->` to pass in the first argument (pipe-first) */
3213->div(24); /* - : int = 8 */
322/* - This is equivalent to div(3, 24); */
323
32436->divBySix; /* - : int = 6 */
325/* - This is equivalent to divBySix(36); */
326
327/* Use `|>` to pass in the last argument (pipe-last) */
32824 |> div(3); /* - : int = 8 */
329/* - This is equivalent to div(3, 24); */
330
33136 |> divBySix; /* - : int = 6 */
332/* - This is equivalent to divBySix(36); */
333
334/* Pipes make it easier to chain code together */
335let addOne = a => a + 1;
336let divByTwo = a => a / 2;
337let multByThree = a => a * 3;
338
339let pipedValue = 3->addOne->divByTwo->multByThree; /* - : int = 6 */
340
341/*----------------------------------------------
342 * Control Flow & Pattern Matching
343 *----------------------------------------------
344 */
345
346/* > If-else */
347/* In Reason, `If` is an expression when evaluate will return the result */
348
349/* greeting will be "Good morning!" */
350let greeting = if (true) {"Good morning!"} else {"Hello!"};
351
352/* Without an else branch the expression will return `unit` or `()` */
353if (false) {
354 showDialog(~message="Are you sure you want to leave?");
355};
356/* - Because the result will be of type `unit`, both return types should be of
357 the same type if you want to assign the result. */
358
359/* > Destructuring */
360/* Extract properties from data structures easily */
361
362let aTuple = ("Teacher", 101);
363
364/* We can extract the values of a tuple */
365let (name, classNum) = aTuple;
366
367/* The properties of a record can be extracted too */
368type person = {
369 firstName: string,
370 age: int,
371};
372let bjorn = {firstName: "Bjorn", age: 28};
373
374/* The variable names have to match with the record property names */
375let {firstName, age} = bjorn;
376
377/* But we can rename them like so */
378let {firstName: bName, age: bAge} = bjorn;
379
380let {firstName: cName, age: _} = bjorn;
381
382/* > Switch
383 Pattern matching with switches is an important tool in Reason
384 It can be used in combination with destructuring for an expressive and
385 concise tool */
386
387/* Lets take a simple list */
388let firstNames = ["James", "Jean", "Geoff"];
389
390/* We can pattern match on the names for each case we want to handle */
391switch (firstNames) {
392| [] => "No names"
393| [first] => "Only " ++ first
394| [first, second] => "A couple of names " ++ first ++ "," ++ second
395| [first, second, third] =>
396 "Three names, " ++ first ++ ", " ++ second ++ ", " ++ third
397| _ => "Lots of names"
398};
399/* - The `_` is a catch all at the end, it signifies that we don't care what
400 the value is so it will match every other case */
401
402/* > When clause */
403
404let isJohn = a => a == "John";
405let maybeName = Some("John");
406
407/* When can add more complex logic to a simple switch */
408let aGreeting =
409 switch (maybeName) {
410 | Some(name) when isJohn(name) => "Hi John! How's it going?"
411 | Some(name) => "Hi " ++ name ++ ", welcome."
412 | None => "No one to greet."
413 };
414
415/* > Exception */
416
417/* Define a custom exception */
418exception Under_Age;
419
420/* Raise an exception within a function */
421let driveToTown = (driver: person) =>
422 if (driver.age >= 15) {
423 "We're in town";
424 } else {
425 raise(Under_Age);
426 };
427
428let evan = {firstName: "Evan", age: 14};
429
430/* Pattern match on the exception Under_Age */
431switch (driveToTown(evan)) {
432| status => print_endline(status)
433| exception Under_Age =>
434 print_endline(evan.firstName ++ " is too young to drive!")
435};
436
437/* Alternatively, a try block can be used */
438/* - With Reason exceptions can be avoided with optionals and are seldom used */
439let messageToEvan =
440 try (driveToTown(evan)) {
441 | Under_Age => evan.firstName ++ " is too young to drive!"
442 };
443
444/*----------------------------------------------
445 * Object
446 *----------------------------------------------
447 * Objects are similar to Record types but aren't as rigid
448 * An object resembles a class
449 */
450
451/* An object may be typed like a record but contains a dot */
452type surfaceComputer = {
453 .
454 color: string,
455 capacity: int,
456};
457/* - A single dot signifies a closed object, an object that uses this type
458 must have the exact shape */
459
460let surfaceBook: surfaceComputer = {pub color = "blue"; pub capacity = 512};
461
462/* But an object doesn't require a type */
463let house = {
464 /* A private property */
465 val temp = ref(18.0);
466 /* Public properties */
467 pub temperature = temp;
468 /* A private method only accessible from within house */
469 pri setThermostat = v => temp := v;
470 /* A public method that calls the private setThermostat method */
471 pub arriveHome = () => this#setThermostat(22.0)
472};
473
474house#temperature; /* - : float = 18. */
475house#arriveHome();
476house#temperature; /* - : float = 22. */
477
478/*----------------------------------------------
479 * Module
480 *----------------------------------------------
481 * Modules are used to organize your code and provide namespacing.
482 * Each file is a module by default
483 */
484
485/* Create a module */
486module Staff = {
487 type role =
488 | Delivery
489 | Sales
490 | Other;
491 type member = {
492 name: string,
493 role,
494 };
495
496 let getRoleDirectionMessage = staff =>
497 switch (staff.role) {
498 | Delivery => "Deliver it like you mean it!"
499 | Sales => "Sell it like only you can!"
500 | Other => "You're an important part of the team!"
501 };
502};
503
504/* A module can be accessed with dot notation */
505let newEmployee: Staff.member = {name: "Laura", role: Staff.Delivery};
506
507/* Using the module name can be tiresome so the module's contents can be opened
508 into the current scope with `open` */
509open Staff;
510
511let otherNewEmployee: member = {name: "Fred", role: Other};
512
513/* A module can be extended using the `include` keyword, include copies
514 the contents of the module into the scope of the new module */
515module SpecializedStaff = {
516 include Staff;
517
518 /* `member` is included so there's no need to reference it explicitly */
519 let ceo: member = {name: "Reggie", role: Other};
520
521 let getMeetingTime = staff =>
522 switch (staff) {
523 | Other => 11_15 /* - : int = 1115; Underscores are for formatting only */
524 | _ => 9_30
525 };
526};