Reason

Личный сайт Go-разработчика из Казани

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};

Further Reading