Format basicStructures $ refmt ./input.re /* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */ let run = () => { TestUtils.printSection("Basic Structures"); }; while (something) { print_string("You're in a while loop"); print_newline(); }; for (i in 0 to 5) { print_int(i); print_newline(); for (i in 10 downto 0) { print_string( "Counting in reverse direction", ); print_newline(); }; }; for (i in 0 to endOfRangeMustBeSimple(expr, soWrap)) { print_int(i); print_newline(); for (i in theSame(isTrue, ofThe, startOfRange) downto 0) { print_string( "Counting in reverse direction", ); print_newline(); }; }; let x = foo^ ^.bar^; let x = foo.bar^; let x = foo#bar^; let x = foo^.bar^; let x = (foo^)#bar^; /* Prefix operators: * ! followed by zero or more appropriate_operator_suffix_chars (see the * lexer). * ? or ~ followed by at least one appropriate_operator_suffix_chars. */ let x = !(!(!foo)).bar; let x = !foo.bar; let x = !foo#bar; let x = !(!foo).bar; let x = !(!foo)#bar; let x = !(!foo.bar); let x = ?!(!foo.bar); let x = ! ?!foo.bar; let x = ~!(!foo.bar); let x = ! ~!foo.bar; let x = ~! ~!foo.bar; let x = !!foo.bar; let x = !!foo#bar; let x = !~foo.bar; let x = !~foo#bar; let noParensNeeded = !blah.foo.bar; let parensNeededAroundFirst = (!blah).foo.bar; let parensNeededAroundSecond = (!blah.foo).bar; let noParensNeeded = !blah#foo#bar; let parensNeededAroundFirst = (!blah)#foo#bar; let parensNeededAroundSecond = (!blah#foo)#bar; let parensWithSpaceNeededAroundFirst = (!(!blah))#foo#bar; let parensWithSpaceNeededAroundSecond = (!(!blah#foo))#bar; let parensWithSpaceNeededAroundFirst = (?!(+ blah))#foo#bar; let parensWithSpaceNeededAroundSecond = (?!(+ blah#foo))#bar; let x = !(!foo.bar); let x = !(!foo#bar); let x = (-10); let x = (-5.0); let x = Some(-10); let x = Some(-5.0); let lazy(x) = 10; let lazy((x: int)) = 10; let lazy([]) = 10; let lazy(true) = 10; let lazy(#x) = 10; let lazy(`Variant) = 10; let lazy(`variant) = 10; let lazy('0' .. '9') = 10; let lazy((lazy(true))) = 10; let lazy([%extend]) = 10; /* Test precedence on access sugar */ let x = arr^[0]; let x = arr^[0]; let x = str^.[0]; let x = str^.[0]; let x = arr^[0] = 1; let x = arr^[0] = 1; /* Comments */ /*Below is an empty comment*/ /**/; /** IF *============================================================================ */; let (/++) = (+); /* // indicates the start of a comment, not an infix op */ let something = if (self.ext.logSuccess) { print_string("Did tap"); print_newline(); }; let logTapSuccess = self => if (self.ext.logSuccess) { print_string("Did tap"); print_newline(); } else { (); }; let logTapSuccess = self => if (self.ext.logSuccess) { print_string("Did tap"); print_newline(); }; (!data).field = true; (!data).field1.field2 = true; (!data.field1).field2 = true; (!data).field1.field2 = true; (!data.field1).field2 = true; let loop = (appTime, frameTime) => { if (hasSetup.contents) { setupScene(); renderIntoTop(); hasSetup.contents = true; }; process(appTime, frameTime); }; /* These parens should be kept around the entire last if/then/else */ if (something) { if (somethingElse) {()} else {"blah"}; }; /* These parens should be kept around just the last if/then*/ if (something) { if (somethingElse) {()} else {"blah"}; }; /* Parens should be generated to wrap the entire final if then else. * To test that it's being parsed correclty, should print "one". */ if (true) { if (true) { print_string("one"); } else { print_string("two"); }; }; /* Should print two */ if (true) { if (false) { print_string("one"); } else { print_string("two"); }; }; /* Should not print */ if (false) { if (true) { print_string("one"); } else { print_string("two"); }; }; /* Should wrap (if a > b then a else b). * printer( */ let printIfFirstArgGreater = true; let result = if (printIfFirstArgGreater) { (a, b) => if (a > b) { print_string("a > b"); } else { print_string("b >= a"); }; } else if ({ (a, b) => if (a > b) { print_string("b < a"); } else { print_string("a <= b"); }; }) { print_string( "That could never possibly type check", ); print_newline(); }; let myRecord = { nestedRecord: { anotherNestedRecord: (instaComp, displayRect) => if (Graphics.cgRectIntersectsWithSlop( defaultCompositeTimerRectSlop, instaComp.relativeRect, displayRect, )) { IoEligible; } else { IoInelibleButTryComposition; }, }, }; if (printIfFirstArgGreater) { (a, b) => if (a > b) { print_string("a > b"); }; } else { (a, b) => if (a > b) { print_string("b < a"); }; }; /* Should Be Parsed As: Cleary a type error, but at least the parsing makes that clear */ if (printIfFirstArgGreater) { (a, b) => if (a > b) { print_string("a > b"); } else { (a, b) => if (a > b) { print_string("b < a"); }; }; }; (a, b) => if (a > b) { print_string("a > b"); }; /* What you probably wanted was: */ if (printIfFirstArgGreater) { (a, b) => if (a > b) { print_string("a > b"); }; } else { (a, b) => if (a > b) { print_string("b < a"); }; }; /* Mutative if statement: Not used to evaluate to something. */ if (10 < 100) { let msg = "If there was any doubt, 10 is in fact less than 100."; print_string(msg); } else { let msg = "All bets are off."; print_string(msg); }; if (10 < 100) { print_string( "If there was any doubt, 10 is in fact less than 100.", ); } else { print_string("All bets are off."); }; /** TYPE CONSTRAINTS *============================================================================ */; let x: int = 10; let x: int = 10; let (x: int) = 10; let (x: int) = (10: int); /* let (x:int) = (10:string); */ /* let (x:string) = ("hello":int); */ /** TUPLES *============================================================================ */; /* In Reason, types look like the data they model! Tuples are no exception. */ type pairOfInts = (int, int); let (letBindingWithTypeConstraint: int) = 10; let (tupleItem: int, withTypeConstraint: int) = ( 10, 20, ); /* To make sure that tuple field annotations are annotating the entire field */ let _dummyFunc = x => 10; let annotatingFuncApplication = ( _dummyFunc("a"): int, _dummyFunc("a"): int, ); /* Pretty printer might stick the [int] at the label. */ let annotatingSingleFuncApplication: int = _dummyFunc("a"); /* So lets try a place where it won't */ let annotatingSingleFuncApplication = { /* Commenting a let binding. */ let a = 100; /* Commenting another let binding. */ let int = 200; /* * This demonstrates why named arguments cannot simply have the form (func * arg:val) - it is indistinguishable from a type constraint. */ 2 + (_dummyFunc(a): int); }; let ( tupleItem: int, constrainedWithoutGrouping: int, ) = ( 10, 20, ); let (tupleItem, withOutsideTypeConstraint): ( int, int, ) = ( 10, 20, ); /* Trailing commas */ let trailingCommaAccepted = (1, 2); let moreTrailing = (1, 2, 3, 4, 5, 7); /** Immutable Lists * ============================================================================ */; /* Anatomy: -Head- --------- Tail--------- nil: You can't see nil */ let x: list(int) = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let hd = "appendedToHead"; let tl = ["listTo", "append", "to"]; /* To push *one* and only *one* item to the front of a list - use [hd, ...tl] */ let result: list(string) = [hd, ...tl]; /* Is the same as writing */ let result: list(string) = [ "appendedToHead", "listTo", "append", "to", ]; /* To operate on lists, use pattern matching */ let rec size = fun | [] => 0 | [hd, ...tl] => 1 + size(tl); /* Optimize for tail recursion */ let rec size = (soFar, lst) => switch (lst) { | [] => 0 | [hd, ...tl] => size(soFar + 1, tl) }; let nestedMatch = lstLst => switch (lstLst) { | [hd, ...tl] when false => 10 | [hd, ...tl] => switch (tl) { | [] => 0 + 0 | [tlHd, ...tlTl] => 0 + 1 } | [] => 0 }; let nestedMatchWithWhen = lstLst => switch (lstLst) { | [hd, ...tl] when false => 10 | [hd, ...tl] when true => switch (tl) { | [] when false => 0 + 0 | [] when true => 0 + 0 | [tlHd, ...tlTl] => 0 + 1 } | [] => 0 }; /** * Aliasing with "as" during matches. */; type mine = | MyThing(int) | YourThing(int); /* * Reason parses "as" aliases differently than OCaml. */ let ppp = switch (MyThing(20)) { | MyThing(x) as ppp | YourThing(x) as ppp => ppp }; let MyThing(_) as ppp | YourThing(_) as ppp = ppp; /* * in order to achieve the previous example in ocaml, you would have to group * as: */ let ppp = switch (MyThing(20)) { | MyThing(x) as ppp | YourThing(x) as ppp => ppp }; let MyThing(_) as ppp | YourThing(_) as ppp = ppp; /* * But this isn't needed in Reason because OR patterns have much lower * precedence - they should be pretty printed in the same way. */ /* TODO: */ /* let rec nestedMatch lstLst => match lstLst with { */ /* hd::tl: match tl with { */ /* []: 0 + 0, */ /* tlHd::tlTl: 0 + 1, */ /* }, */ /* []: 0 */ /* }; */ /* */ /** ARRAYS * ============================================================================ * Arrays are weird looking. Usually you want lists because they support pattern * matching - that's why they have nicer syntax - to entice you. But if you want * random access and better control over memory layout, use arrays. */; let emptyArray = [||]; let arrayWithOne = [|10|]; let arrayWithTwo = [|10, 10|]; let secondItem = arrayWithTwo[1]; /* Getting And Setting: Yeah, we should really change this */ /* Get an array item at index 1 */ let secondItem = arrayWithTwo[1]; /* Set an array item at index 1 */ arrayWithTwo[1] = 300; /** * STRINGS * ============================================================================ * The language supports mutating strings, but that should not be depended upon. */; let myString = "asdf"; myString.[2] = '9'; /* Replacing a character: I could do without this sugar */ /* FUNCTIONS *============================================================================= */ /* TYPE ANNOTATIONS * ============================================================================= */ let one = 900; let two = 10000; /* Tuple expressions can be annotated without additional paren wrapping */ let myTuple = (one: int, two: int); type myTupleType = (int, int); let myTuple: myTupleType = myTuple; /* Anything *outside* of a tuple, must still be annotated within parens. */ let myTuple: myTupleType = (one: int, two: int); /* Now functions that accept a single argument being a tuple look familiar */ let addValues = (a: int, b: int) => { a + b; }; let addValues = (a: int, b: int) => { a + b; }; let myFunction = (a: int, b: int): int => a + b; let functionReturnValueType = (i: int, s: string): (int => int) => x => x + 1; let curriedFormOne = (i: int, s: string) => s ++ string_of_int(i); let curriedFormTwo = (i: int, x: int): (int, int) => ( i, x, ); /* let nonCurriedFormTwo = fun (i:int, x:int) (:(int, int)) => (i, x); */ let curriedFormThree = (i: int, (a: int, b: int): (int, int)) : (int, int, int) => ( i, a, b, ); /* let nonCurriedFormThree = fun (i:int, (a:int, b:int):(int, int)) (:(int, int, int)) => (i, a, b); */ /** TODO: But this, however doesn't work. * let (myCurriedFunc: int => int) a => a; * Note: This is likely because only "simple patterns" are accepted as constraints * in let bindings - that may be easy to change. */; type myFuncType = (int, int) => int; let myFunc: myFuncType = (a, b) => a + b; let funcWithTypeLocallyAbstractTypes = ( type atype, type btype, a, b, c: (atype, btype) => unit, ) => c(a, b); /* Checks that function types aren't unnecessary wrapped */ type a = unit => unit; type b = | Foo(unit => unit) | Bar(unit => unit, unit => unit, (a, b) => c) | Baz( unit => unit, unit => unit, (a, b) => c, ); type c = | Foo((a, b) => unit) | Bar((a, b) => unit); type d = [> | `Foo(unit => unit)]; /** * Records: *============================================================================= */; type withThreeFields = { name: string, age: int, occupation: string, }; let testRecord = { name: "joe", age: 20, occupation: "engineer", }; let anotherRecord = { ...testRecord, name: "joe++", age: testRecord.age + 10, }; let makeRecordBase = () => { name: "Joe", age: 30, occupation: "Engineer", }; let anotherRecord = { /* These parens should be evaporated. */ ...makeRecordBase(), name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { /* Comments should be correctly placed before ... expression */ ...makeRecordBase(), /* Comment after record extension */ name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { /* Currently, type annotations must be wrapped in parens - that's easy to improve */ ...(makeRecordBase(): withThreeFields), name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { /* This is meaningless, sure */ ...someArray.[0] = 20, name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { ... SomeReally.longFunctionCall({ passingRecordField: 0, andThisOtherRecordField: 10, }), name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { ... SomeReally.longFunctionCall( withArguments, thatWrap: bool, ), name: "joe++", age: testRecord.age + 10, }; let anotherRecord = { ... SomeReally.longFunctionCall( withArg, [ "and", "final", "list", "that", "should", "break", ], ), name: "joe++", age: testRecord.age + 10, }; /* Record type punning */ type props = {title: string}; type state = unit; type component = {props}; type component2 = { props, state, updater: unit, }; type component3 = { props: M.props, state, }; type mutableComponent = {mutable props}; type mutabeleComponent2 = { mutable props, mutable state, style: int, }; /* Don't pun parameterized types */ type description('props) = { element: string, tag: tag('props), }; /* Don't pun types from other modules */ module Foo = { type bar = {foo: Baz.foo}; }; /* Don't pun field names that aren't "simple" */ type foo = { bar: Baz.bar, qux, fooo: Fooo.fooo, }; let moreFoo = { bar: Baz.bar, qux, fooo: Fooo.fooo, }; /* record value punning */ let props = { title: "hi" }; /* no punning available for a single field. Can't tell the difference with a scope + expression */ let componentA = { props: props }; /* pun for real */ let componentB = { props, state: (), }; /* pun fields with module prefix too */ let foo = { Foo.foo: foo }; let bar = { Foo.foo, bar: 1, }; let bar = { bar: 1, Foo.foo, }; let bar = { Foo.foo, Bar.bar, }; ({ M.x, y }) => 1; switch (foo) { | { y: 1, M.x } => 2 }; /* Requested in #566 */ let break_after_equal = no_break_from_here(some_call(to_here)); /* Pexp_letexception */ let () = { exception E; raise(E); }; /* # 1587: don't print fun keyword when printing Pexp_fun in a record expression */ { contents: () => ((): unit) }; /* #1556: Always break nested record/obj */ let z = { a: { b: c, d: e, }, f: g, }; let z = { a: { "b": c, "d": e, }, f: g, }; let z = { a: { pub b = c; pub d = e }, f: g, }; let z = { "a": { "b": c, "d": e, }, "f": g, }; let z = { "a": { b: c, d: e, }, "f": g, }; let z = { "a": { pub b = c; pub d = e }, "f": g, }; /** * Unnecessary parens should be removed. */ let unitLambda = () => (); let identifierLambda = a => (); let underscoreLambda = _ => (); it("should remove parens", a => { print_string("did it work?"); print_string("did it work?"); }); /* https://github.com/facebook/reason/issues/1554 */ (curNode^)##childNodes; foo(preserveBraces => {inCallback}); foo(preserveBraces => {inFirstPos}, secondArg); foo( oneArg, preserveBraces => {inFirstPos}, secondArg, );