/* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */ [@autoFormat let wrap=80; let shift=2;]; Modules.run (); Polymorphism.run (); Variants.run(); BasicStructures.run(); TestUtils.printSection("General Syntax"); /* Won't work! */ /* let matchingFunc a = match a with */ /* `Thingy x => (print_string "matched thingy x"); x */ /* | `Other x => (print_string "matched other x"); x;; */ /* */ let matchingFunc(a) = switch (a) { | `Thingy x => { print_string("matched thingy x"); let zz = 10; zz; } | `Other x => { print_string("matched other x"); x; } }; type firstTwoShouldBeGroupedInParens = ((int) => int, int) => int; type allParensCanBeRemoved = (int) => ((int) => ((int) => int)); type firstTwoShouldBeGroupedAndFirstThree = (((int) => int) => int) => int; /* Same thing now but with type constructors instead of each int */ type firstTwoShouldBeGroupedInParens = ((list(int)) => list(int), list(int)) => list(int); type allParensCanBeRemoved = (list(int)) => ((list(int)) => ((list(int)) => list(int))); type firstTwoShouldBeGroupedAndFirstThree = (((list(int)) => list(int)) => list(int)) => list(int); type myRecordType = { firstTwoShouldBeGroupedInParens: ((int) => int, int) => int, allParensCanBeRemoved: (int) => ((int) => ((int) => int)), firstTwoShouldBeGroupedAndFirstThree: (((int) => int) => int) => int, }; type firstNamedArgShouldBeGroupedInParens = (~first: (int) => int, ~second: int) => int; type allParensCanBeRemoved = (~first: int) => ((~second: int) => ((~third: int) => int)); type firstTwoShouldBeGroupedAndFirstThree = (~first: ((int) => int) => int) => int; /* Same thing now, but with type constructors instead of int */ type firstNamedArgShouldBeGroupedInParens = (~first: (list(int)) => list(int)) => (~second: list(int)) => list(int); type allParensCanBeRemoved = (~first: list(int)) => (~second: list(int)) => (~third: list(int)) => list(int); type firstTwoShouldBeGroupedAndFirstThree = (~first: ((list(int)) => list(int)) => list(int)) => list(int); type firstNamedArgShouldBeGroupedInParens = (~first: ((int) => int)=?, ~second: list(int)=?) => int; /* The arrow necessitates parens around the next two args. The ? isn't what * makes the parens necessary. */ type firstNamedArgShouldBeGroupedInParensAndSecondNamedArg = (~first: ((int) => int)=?, ~second: ((int) => int)=?) => int; type allParensCanBeRemoved = (~first: int=?, ~second: int=?, ~third: int=?) => int; type firstTwoShouldBeGroupedAndFirstThree = (~first: (((int) => int) => int)) => int; type noParens = (~one: int, int, int, ~two: int) => int; type noParensNeeded = (~one: int) => ((int) => ((int) => ((~two: int) => int))); type firstNamedArgNeedsParens = (~one: (int, int) => int, ~two: int) => int; /* Now, let's try type aliasing */ /* Unless wrapped in parens, types between arrows may not be aliased, may not * themselves be arrows. */ type parensRequiredAroundFirstArg = (list(int) as 'a) => int as 'a; type parensRequiredAroundReturnType = (list(int) as 'a) => (int as 'a); type parensRequiredAroundReturnType = (list(int) as 'a) => (int as 'a) as 'b; type noParensNeededWhenInTuple = (list(int) as 'a, list(int) as 'b) as 'entireThing; type myTypeDef('a) = list('a); type instatiatedTypeDef = (myTypeDef(int)) => int; /* Test a type attribute for good measure */ /* We should clean up all of the attribute tagging eventually, but for now, * let's make it super ugly to get out of the way of all the formatting/parsing * implementations (fewer conflicts during parsing, fewer edge cases during * printing). */ type something = (int, ([@lookAtThisAttribute] int)); type longWrappingTypeDefinitionExample = M_RK__G.Types.instance ( TGRecognizer.tGFields(unit,unit), TGRecognizer.tGMethods(unit,unit) ) ; type semiLongWrappingTypeDefinitionExample = M_RK__Gesture.Types.instance ( TGRecognizerFinal.tGFields, TGRecognizerFinal.tGMethods ); type semiLongWrappingTypeWithConstraint = M_RK__Gesture.Types.instance('a, TGRecognizerFinal.tGFields, TGRecognizerFinal.tGMethods ) constraint 'a = (unit, unit) ; type onelineConstrain = 'a constraint 'a = int; /* This must be in trunk but not in this branch of OCaml */ /* type withNestedRecords = MyConstructor {myField: int} */ type colors = | Red(int) | Black(int) | Green(int); /* Another approach is to require declared variants to wrap any record */ /* type myRecord = MyRecord {name: int}; */ /* let myValue = MyRecord {name: int}; */ /* This would force importing of the module */ /* This would also lend itself naturally to pattern matching - and avoid having to use `.` operator at all since you normally destructure. */ type nameBlahType = {nameBlah: int}; let myRecord = {nameBlah: 20}; let myRecordName = myRecord.nameBlah; let {nameBlah}:nameBlahType = {nameBlah: 20}; print_int(nameBlah); let {nameBlah: aliasedToThisVar}:nameBlahType = {nameBlah: 20}; print_int(aliasedToThisVar); let desiredFormattingForWrappedLambda: (int, int, int) => nameBlahType = /* fun is pre- /firstarg\ fix /-coupled--\ |-\ /-to-prefix--\ */ fun(curriedArg, anotherArg, lastArg) => { nameBlah: 10 }; type longerInt = int; let desiredFormattingForWrappedLambdaWrappedArrow: (longerInt, longerInt, longerInt) => nameBlahType = /* fun is pre- /firstarg\ fix /-coupled--\ |-\ /-to-prefix--\ */ fun(curriedArg, anotherArg, lastArg) => { nameBlah: 10 }; let desiredFormattingForWrappedLambdaReturnOnNewLine = /* fun is pre- /firstarg\ fix /-coupled--\ |-\ /-to-prefix--\ */ fun(curriedArg, anotherArg, lastArg) => { nameBlah: 10 }; /* let is pre- fix /-function binding name---\ |-\ / is coupled to prefix \ */ let desiredFormattingForWrappedSugar (curriedArg, anotherArg, lastArg) { nameBlah: 10 }; /* let is pre- fix /-function binding name---\ |-\ / is coupled to prefix \ */ let desiredFormattingForWrappedSugarReturnOnNewLine (curriedArg, anotherArg, lastArg) { nameBlah: 10 }; /* let : type t1 t2. t1 * t2 list -> t1 = ... let rec f : 't1 't2. 't1 * 't2 list -> 't1 = fun (type t1) (type t2) -> (... : t1 * t2 list -> t1) */ type point = {x: int, y:int}; type point3D = {x: int, y:int, z:int}; let point2D = { x: 20, y: 30 }; let point3D:point3D = { x: 10, y: 11, z: 80, /* Optional Comma */ }; let printPoint (p:point) { print_int(p.x); print_int(p.y); }; let addPoints (p1:point, p2:point) { x: p1.x + p2.x, y: p1.y + p2.y }; let res1 = printPoint(point2D); let res2 = printPoint({x:point3D.x, y:point3D.y}); /* When () were used to indicate sequences, the parser used seq_expr not only for grouping sequences, but also to form standard precedences. /------- sequence_expr ------\ let res3 = printPoint (addPoints (point2D, point3D)); Interestingly, it knew that tuples aren't sequences. To move towards semi delimited, semi-terminated, braces-grouped sequences: while allowing any non-sequence expression to be grouped on parens, we make an explicit rule that allows one single non-semi ended expression to be grouped in parens. Actually: We will allow an arbitrary number of semi-delimited expressions to be wrapped in parens, but the braces grouped semi delimited (sequence) expressions must *also* be terminated with a semicolon. This allows the parser to distinguish between let x = {a}; /* Record {a:a} */ let x = {a;}; /* Single item sequence returning identifier {a} */ */ let res3 = printPoint (addPoints (point2D, {x:point3D.x, y:point3D.y})); type person = {age: int, name: string}; type hiredPerson = {age: string, name: string, dateHired: int}; let o: (person) = {name: "bob", age: 10}; /* Parens needed? Nope! */ let o: person = {name: "bob", age: 10}; let printPerson (p: person) { let q: person = p; p.name ++ p.name; }; /* let dontParseMeBro x y:int = x = y;*/ /* With this unification, anywhere eyou see `= fun` you can just ommit it */ let blah = fun(a) => a; /* Done */ let blah (a) = a; /* Done (almost) */ let blah = fun(a,b) => a; /* Done */ let blah (a, b) = a; /* Done (almost) */ /* More than one consecutive pattern must have a single case */ type blah = {blahBlah: int}; let blah = fun (a, {blahBlah}) => a; let blah (a, {blahBlah}) = a; module TryToExportTwice = { let myVal = "hello"; }; /* Unifying top level module syntax with local module syntax is probably a bad idea at the moment because it makes it more difficult to continue to support `let .. in` bindings. We can distinguish local modules for `let..in` that just happen to be defined at the top level (but not exported). let MyModule = {let myVal = 20;} in MyModule.x Wait, where would this ever be valid, even if we continued to support `let..in`? */ let onlyDoingThisTopLevelLetToBypassTopLevelSequence = { let x = { print_int(1); print_int(20); /* Missing trailing SEMI */ }; let x = { print_int(1); print_int(20); /* Ensure missing middle SEMI reported well */ print_int(20); }; let x = { print_int(1); print_int(20); 10; /* Comment in final position */ }; /* Missing final SEMI */ x + x; }; type hasA = {a:int}; let a = 10; let returnsASequenceExpressionWithASingleIdentifier () {a}; let thisReturnsA () {a;}; let thisReturnsAAsWell () = a; let recordVal:int = thisReturnsARecord().a; Printf.printf("\nproof that thisReturnsARecord: %n\n", recordVal); Printf.printf("\nproof that thisReturnsA: %n\n", thisReturnsA()); /* Pattern matching */ let blah = fun(arg) => switch (arg) { /* Comment before Bar */ | /* Comment between bar/pattern */ Red(_) => 1 /* Comment Before non-first bar */ | /* Comment betwen bar/pattern */ Black(_) => 0 | Green(_) => 0 }; /* Any function that pattern matches a multicase match is interpretted as a * single arg that is then matched on. Instead of the above `blah` example:*/ let blah = fun | Red(_) => 1 | Black(_) => 0 | Green(_) => 1; /* `fun a => a` is read as "a function that maps a to a". Then the */ /* above example is read: "a function that 'either maps' Red to.. or maps .." */ /* Thc00f564e first bar is read as "either maps" */ /* Curried form is not supported: let blah x | Red _ => 1 | Black _ => 0; Theres no sugar rule for dropping => fun, only = fun */ /* let blahCurriedX x => fun /* See, nothing says we can drop the => fun */ */ /* |(Red x | Black x | Green x) => 1 /* With some effort, we can ammend the sugar rule that would */ */ /* | Black x => 0 /* Allow us to drop any => fun.. Just need to make pattern matching */ */ /* | Green x => 0; /* Support that */ */ /* */ let blahCurriedX(x) = fun | Red(x) | Black(x) | Green(x) => 1 /* With some effort, we can ammend the sugar rule that would */ | Black(x) => 0 /* Allow us to drop any => fun.. Just need to make pattern matching */ | Green(x) => 0; /* Support that */ let sameThingInLocal = { let blahCurriedX(x) = fun | Red(x) | Black(x) | Green(x) => 1 /* With some effort, we can ammend the sugar rule that would */ | Black(x) => 0 /* Allow us to drop any => fun.. Just need to make pattern matching */ | Green(x) => 0; /* Support that */ blahCurriedX; }; /* This should be parsed/printed exactly as the previous */ let blahCurriedX(x) = fun | Red(x) | Black(x) | Green(x) => 1 | Black(x) => 0 | Green(x) => 0; /* Any time there are multiple match cases we require a leading BAR */ let v = Red(10); let (Black(x) | Red(x) | Green(x)) = v; /* So this NON-function still parses */ /* This doesn't parse, however (and it doesn't in OCaml either): let | Black(x) | Red(x) | Green(x) = v; */ print_int(x); /* Scoping: Let sequences. Familiar syntax for lexical ML style scope and sequences. */ let res = { let a = "a starts out as"; { print_string(a); let a = 20; print_int(a); }; print_string(a); }; let res = { let a = "first its a string"; let a = 20; print_int(a); print_int(a); print_int(a); }; let res = { let a = "a is always a string"; print_string(a); let b = 30; print_int(b); }; /* let result = LyList.map((fun | [] => true | _ => false), []); */ /* OTHERWISE: You cannot tell if a is the first match case falling through or * a curried first arg */ /* let blah = fun a | patt => 0 | anotherPatt => 1; */ /* let blah a patt => 0 | anotherPatt => 1; */ /*simple pattern EQUALGREATER expr */ let blah ( a, {blahBlah}) = a; /* match_case */ /* pattern EQUALGREATER expr */ let blah = fun |Red(_) => 1 |Black(_)=> 0 |Green(_)=> 0; /* Won't work! */ /* let arrowFunc = fun a b => print_string "returning aplusb from arrow"; a + b;; */ let arrowFunc = fun(a,b)=> {print_string("returning aplusb from arrow"); a + b;}; let add(a,b) { let extra = {print_string("adding"); 0;}; let anotherExtra = 0; extra + a + b + anotherExtra; }; (print_string (string_of_int (add(4,34)))); let dummy(_) = 10; dummy(res1); dummy(res2); dummy(res3); /* Some edge cases */ let myFun (firstArg, Red(x) | Black(x) | Green(x)) = firstArg + x; let matchesWithWhen(a) = switch (a) { | Red(x) when 1 > 0 => 10 | Red(_) => 10 | Black(x) => 10 | Green(x) => 10 }; let matchesWithWhen = fun | Red(x) when 1 > 0 => 10 | Red(_) => 10 | Black(x) => 10 | Green(x) => 10; let matchesOne (`Red x) = 10; /* Typical OCaml would make you *wrap the functions in parens*! This is because it can't tell if a semicolon is a sequence operator. Even if we had records use commas to separate fields, */ type adders = { addTwoNumbers: (int, int) => int, addThreeNumbers: (int, int, int) => int, addThreeNumbersTupled: ((int, int, int)) => int }; let myRecordWithFunctions = { addTwoNumbers: (a,b) => a + b, addThreeNumbers: (a,b,c) => a + b + c, addThreeNumbersTupled: ((a, b, c)) => a + b + c }; let result = myRecordWithFunctions.addThreeNumbers(10, 20, 30); let result = myRecordWithFunctions.addThreeNumbersTupled((10, 20, 30)); let lookTuplesRequireParens = (1, 2); /* let thisDoesntParse = 1, 2; */ let tupleInsideAParenSequence = { print_string("look, a tuple inside a sequence"); let x = 10; (x, x); }; let tupleInsideALetSequence = { print_string("look, a tuple inside a sequence"); let x = 10; (x, x); }; /* We *require* that function return types be wrapped in parenthesis. In this example, there's no ambiguity */ let makeIncrementer (delta:int) : (int)=>int = (a) => a + delta; /* We could even force that consistency with let bindings - it's allowed currently but not forced. */ let (myAnnotatedValBinding:int) = 10; /* Class functions (constructors) and methods are unified in the same way */ class classWithNoArg { pub x = 0; pub y = 0; }; /* This parses but doesn't type check class myClass init => object pub x => init pub y => init end; */ let myFunc (a:int, b:int) : (int, int) = (a, b); let myFunc (a:int, b:int) : list(int) = [1]; let myFunc (a:int, b:int) : point { x: a, y: b }; let myFunc (a:int, b:int) : point { x: a, y: b }; type myThing = (int, int); type stillARecord = {name: string, age: int}; /* Rebase latest OCaml to get the following: And fixup `generalized_constructor_arguments` according to master. */ /* type ('a, 'b) myOtherThing = Leaf {first:'a, second: 'b} | Null; */ type branch('a,'b) = {first: 'a, second: 'b}; type myOtherThing('a,'b) = Leaf (branch('a,'b)) | Null; type yourThing = myOtherThing(int,int); /* Conveniently - this parses exactly how you would intend! No *need* to wrap in an extra [], but it doesn't hurt */ /* FIXME type lookAtThesePolyVariants = list [`Red] ; */ /* FIXME type bracketsGroupMultipleParamsAndPrecedence = list (list (list [`Red])); */ /* FIXME type youCanWrapExtraIfYouWant = (list [`Red]); */ /* FIXME type hereAreMultiplePolyVariants = list [`Red | `Black]; */ /* FIXME type hereAreMultiplePolyVariantsWithOptionalWrapping = list ([`Red | `Black]); */ /* /* Proposal: ES6 style lambdas: */ /* Currying */ let lookES6Style = (`Red x) (`Black y) => { }; let lookES6Style (`Red x) (`Black y) => { }; /* Matching the single argument */ let lookES6Style = oneArg => match oneArg with | `Red x => x | `Black x => x; /* The "trick" to currying that we already have is basically the same - we just * have to reword it a bit: * From: * "Any time you see [let x = fun ...] just replace it with [let x ...]" * To: * "Any time you see [let x = ... => ] just replace it with [let x ... => ]" */ let lookES6Style oneArg => match oneArg with | `Red x => x | `Black x => x; */ /** Current OCaml Named Arguments. Any aliasing is more than just aliasing! OCaml allows full on pattern matching of named args. */ /* A: let named ~a ~b = aa + bb in B: let namedAlias ~a:aa ~b:bb = aa + bb in C: let namedAnnot ~(a:int) ~(b:int) = a + b in D: let namedAliasAnnot ~a:(aa:int) ~b:(bb:int) = aa + bb in E: let optional ?a ?b = 10 in F: let optionalAlias ?a:aa ?b:bb = 10 in G: let optionalAnnot ?(a:int option) ?(b:int option) = 10 in H: let optionalAliasAnnot ?a:(aa:int option) ?b:(bb:int option) = 10 in /* Look! When a default is provided, annotation causes inferred type of argument to not be "option" since it's automatically destructured (because we know it will always be available one way or another.) */ I: let defOptional ?(a=10) ?(b=10) = 10 in J: let defOptionalAlias ?a:(aa=10) ?b:(bb=10) = 10 in K: let defOptionalAnnot ?(a:int=10) ?(b:int=10) = 10 in \ \ \label_let_pattern opt_default: no longer needed in SugarML L: let defOptionalAliasAnnot ?a:(aa:int=10) ?b:(bb:int=10) = 10 in \ \ \let_pattern: still a useful syntactic building block in SugarML */ /** * In Reason, the syntax for named args uses double semicolon, since * the syntax for lists uses ES6 style [], freeing up the ::. */ let a = 10; let b = 20; /*A*/ let named (~a as a, ~b as b) = a + b; type named = (~a: int, ~b: int) => int; /*B*/ let namedAlias (~a as aa, ~b as bb) = aa + bb; let namedAlias (~a as aa, ~b as bb) = aa + bb; type namedAlias = (~a: int, ~b: int) => int; /*C*/ let namedAnnot (~a :int, ~b :int) = 20; /*D*/ let namedAliasAnnot (~a as aa:int,~b as bb:int) = 20; /*E*/ let myOptional (~a=?, ~b=?, ()) = 10; type named = (~a: int=?, ~b: int=?, unit) => int; /*F*/ let optionalAlias (~a as aa=?, ~b as bb=?, ()) = 10; /*G*/ let optionalAnnot (~a as a:int =?, ~b as b:int=?, ()) = 10; /*H*/ let optionalAliasAnnot (~a as aa:int =?, ~b as bb:int=?, ()) = 10; /*I: */ let defOptional (~a as a=10, ~b as b=10, ()) = 10; type named = (~a: int=?, ~b: int=?, unit) => int; /*J*/ let defOptionalAlias (~a as aa=10, ~b as bb=10, ()) = 10; /*K*/ let defOptionalAnnot (~a as a:int=10, ~b as b:int=10, ()) = 10; /*L*/ let defOptionalAliasAnnot(~a as aa:int=10, ~b as bb:int=10, ()) = 10; /*M: Invoking them - Punned */ let resNotAnnotated = named(~a=a,~b=b); /*N:*/ let resAnnotated = (named(~a=a,~b=b):int); /*O: Invoking them */ let resNotAnnotated = named(~a=a,~b=b); /*P: Invoking them */ let resAnnotated = (named(~a=a,~b=b):int); /*Q: Here's why "punning" doesn't work! */ /* Is b:: punned with a final non-named arg, or is b:: supplied b as one named arg? */ let b = 20; let resAnnotated = (named(~a=a,~b=b)); /*R: Proof that there are no ambiguities with return values being annotated */ let resAnnotated = (named(~a=a,b):ty); /*S: Explicitly passed optionals are a nice way to say "use the default value"*/ let explictlyPassed = myOptional(~a=?None,~b=?None); /*T: Annotating the return value of the entire function call */ let explictlyPassedAnnotated = (myOptional(~a=?None,~b=?None):int); /*U: Explicitly passing optional with identifier expression */ let a = None; let explictlyPassed = myOptional(~a=?a,~b=?None); let explictlyPassedAnnotated = (myOptional(~a=?a,~b=?None):int); let nestedLet = { let _ = 1 }; let nestedLet = { let _ = 1; }; let nestedLet = { let _ = 1; () }; let nestedLet = { let _ = 1; 2 }; /* * Showing many combinations of type annotations and named arguments. */ type typeWithNestedNamedArgs = (~outerOne: (~innerOne: int, ~innerTwo: int) => int, ~outerTwo: int) => int; type typeWithNestedOptionalNamedArgs = (~outerOne: (~innerOne: int, ~innerTwo: int) => int=?, ~outerTwo: int=?) => int; type typeWithNestedOptionalNamedArgs = (~outerOne: list(string)=?, ~outerTwo: int=?) => int; let f(~tuple="long string to trigger line break") = (); let x = callSomeFunction (~withArg=10, ~andOtherArg=wrappedArg); let res = { (constraintedSequenceItem:string); (dontKnowWheYoudWantToActuallyDoThis:string); }; let res = { (butTheyWillBePrintedWithAppropriateSpacing : string); (soAsToInstillBestDevelopmentPractices : string); }; let x = [ eachItemInListCanBeAnnotated:int, typeConstraints:float, (tupleConstraints:int, andNotFunctionInvocations:int) ]; let x = [ butWeWillPrint : int, themAsSpaceSeparated : float, (toInfluenceYour : int, developmentHabbits : int) ]; let newRecord = { ...annotatedSpreadRecord:someRec, x: y }; let newRecord = { ...annotatedSpreadRecord : someRec, blah: 0, foo: 1 }; let newRecord = { ...youCanEvenCallMethodsHereAndAnnotate(them): someRec, blah: 0, foo: 1 }; let newRecord = { ...youCanEvenCallMethodsHereAndAnnotate(them,~named=10):someRec, blah: 0, foo: 1 }; let something = (aTypeAnnotation : thing(blah)); let something = (thisIsANamedArg: thing(blah)); let something = (aTypeAnnotation: thing(blah)); let something = (thisIsANamedArg(thing):blah); let something = (typeAnnotation(thing): blah); let newRecord = { ...(heresAFunctionWithNamedArgs(~argOne=i) :annotatedResult), soAsToInstill: 0, developmentHabbits: 1 }; [@thisIsAThing]; let x = 10; /* Ensure that the parenthesis are preserved here because they are * important: */ let something = fun | None => (fun | [] => "emptyList" | [_, ..._] => "nonEmptyList") | Some(_)=> (fun | [] => "emptyList" | [_, ..._] => "nonEmptyList"); /* A | B = X; */ let A | B = X; /* A | (B | C) = X; */ let A | (B | C) = X; /* (A | B) | (C | D) = X; */ let (A | B) | (C | D) = X; /* A | B | (C | D) = X; */ let A | B | (C | D) = X; /* (A | B) | C = X; */ let (A | B) | C = X; /* A | B | C = X; */ let A | B | C = X; /** External function declaration * */ external f : (int) => int = "foo"; let x = {contents: 0}; let unitVal = x.contents = 210; let match = "match"; let method = "method"; let foo(x,~x as bar,~z,~foo as bar,~foo as z) { bar + 2 }; let zzz = myFunc(1, 2, [||]); /* 1492 */ let registerEventHandlers = ( ~window: Window.t, ~mouseDown: option(((~button: Events.buttonStateT, ~state: Events.stateT, ~x: int, ~y: int) => unit))=?, () ) => 1; /* #1320: record destrucuring + renaming */ let x = ({state: state as prevState}) => 1; let x = ({ReasonReact.state: state as prevState}) => 1; /* 1567: optional parens around expr constraint in constructor expression */ Some(x : int); Some((x : int)); Some(x, y: int, b); Some(x, (y: int), b); foo(~x=-. bar); Some((-1), (-1), (-1)); Some(-1, -1, -1); Some(-1g, -1G, -1z); Some((-1g), (-1G), (-1z)); Some(-0.1, -0.1, -0.1); Some((-0.1), (-0.1), (-0.1)); Some(-0.1G, -0.1x, -0.1H); Some((-0.1G), (-0.1x), (-0.1H)); Some([@foo] (-1), [@foo] (-1), [@foo] (-1)); Some([@foo] (-1z), [@foo] (-1z), [@foo] (-1z)); Some([@foo] (-0.1), [@foo] (-0.1), [@foo] (-0.1)); Some([@foo] (-0.1m), [@foo] (-0.1n), [@foo] (-0.1p)); foo(~x=-1, ~y=-2); foo(~x=(-1), ~y=(-2)); foo(~x=-.1, ~y=-.2); foo(~x=(-.1), ~y=(-.2)); foo(~x=-1g, ~y=-1G, ~z=-1z); foo(~x=(-1g), ~y=(-1G), ~z=(-1z)); foo(~x=-0.1G, ~y=-0.1x, ~z=-0.1H); foo(~x=(-0.1G), ~y=(-0.1x), ~z=(-0.1H)); foo(~x=[@foo] (-1), ~y=[@foo] (-1), ~z=[@foo] (-1)); foo(~x=[@foo] (-1z), ~y=[@foo] (-1z), ~z=[@foo] (-1z)); foo(~x=[@foo] (-0.1), ~y=[@foo] (-0.1), ~z=[@foo] (-0.1)); foo(~x=[@foo] (-0.1m), ~y=[@foo] (-0.1n), ~z=[@foo] (-0.1p)); /* Smooth formatting of functions with callbacks as arguments */ funWithCb("text", () => doStuff()); funWithCb([@attr] "text", [@myAttr] () => doStuff()); funWithCb(~text="text", ~f=() => doStuff()); funWithCb(~text=[@attr] "text", ~f=[@myAttr] () => doStuff()); funWithCb(~text="text", ~f=?() => doStuff()); funWithCb(~text=[@attr] "text", ~f=?[@myAttr] () => doStuff()); test("my test", () => { let x = a + b; let y = z + c; x + y }); test([@attr] "my test", [@attr] () => { let x = a + b; let y = z + c; x + y }); test(~desc="my test", ~f=() => { let x = a + b; let y = z + c; x + y }); test(~desc=[@attr] "my test", ~f=[@myAttr] () => { let x = a + b; let y = z + c; x + y }); test(~desc=?"my test", ~f=?() => { let x = a + b; let y = z + c; x + y }); test(~desc=?[@attr] "my test", ~f=?[@attr] () => { let x = a + b; let y = z + c; x + y }); describe("App", () => { test("math", () => { Expect.expect(1+2) |> toBe(3) }); }); describe([@attr] "App", [@attr] () => { test([@attr] "math", [@attr] () => { Expect.expect(1+2) |> toBe(3) }); }); describe(~text="App", ~f=() => test(~text="math", ~f=() => Expect.expect(1 + 2) |> toBe(3) ) ); describe(~text=[@attr] "App", ~f=[@attr] () => test(~text=[@attr] "math", ~f=[@attr] () => Expect.expect(1 + 2) |> toBe(3) ) ); describe(~text=?"App", ~f=?() => test(~text=?"math", ~f=?() => Expect.expect(1 + 2) |> toBe(3) ) ); describe(~text=?[@attr] "App", ~f=?[@attr] () => test(~text=?[@attr] "math", ~f=?[@attr] () => Expect.expect(1 + 2) |> toBe(3) ) ); Thing.map(foo, bar, baz, (abc, z) => MyModuleBlah.toList(argument) ); Thing.map([@attr] foo, bar, baz, [@attr] (abc, z) => MyModuleBlah.toList(argument) ); Thing.map(~a=[@attr] foo, ~b=bar, ~c=?baz, ~f=(abc, z) => MyModuleBlah.toList(argument) ); Thing.map(~a=foo, ~b=bar, ~c=?[@attr] baz, ~f=[@attr] (abc, z) => MyModuleBlah.toList(argument) ); Thing.map(~a=foo, ~b=bar, ~c=?baz, ~f=?(abc, z) => MyModuleBlah.toList(argument) ); Thing.map(~a=foo, ~b=bar, ~c=?[@attr] baz, ~f=?[@attr] (abc, z) => MyModuleBlah.toList(argument) ); Thing.map( foo, bar, baz, foo2, bakjlksjdf, okokokok, (abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( foo, bar, baz, foo2, [@attr] bakjlksjdf, okokokok, [@attr] (abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( ~a=foo, ~b=bar, ~c=baz, ~d=foo2, ~e=bakjlksjdf, ~f=okokokok, ~cb=(abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( ~a=foo, ~b=bar, ~c=[@attr] baz, ~d=foo2, ~e=bakjlksjdf, ~f=okokokok, ~cb=[@attr] (abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( ~a=?foo, ~b=?bar, ~c=?baz, ~d=?foo2, ~e=?bakjlksjdf, ~f=?okokokok, ~cb=?(abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( ~a=?foo, ~b=?bar, ~c=?baz, ~d=?foo2, ~e=?[@attr] bakjlksjdf, ~f=?okokokok, ~cb=?[@attr] (abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); Thing.map( foo, bar, baz, (abc, z) => MyModuleBlah.toList(argument), (abc, z) => MyModuleBlah.toList(argument) ); Thing.map( foo, bar, baz, [@attr] (abc, z) => MyModuleBlah.toList(argument), [@attr] (abc, z) => MyModuleBlah.toList(argument) ); Js.Option.andThen([@bs] w => w#getThing()); Thing.map( ~a=?foo, ~b=?bar, ~c=?baz, ~d=?foo2, ~e=?[@attr] bakjlksjdf, ~f=?okokokok, ~cb=?[@attr] [@attr2] [@aslkdfjlsdjf][@sldkfjlksdjflksjdlkjf] [@sldkflskjdf] (abc, z) => { let x = 1; MyModuleBlah.toList(x, argument); } ); let result = F.call(x => { let x = 123; let y = 2345; doStuff(); }); let result = F.call(x => doStuff(x)); let () = x |> Bigarray.Genarray.get(_, [|1, 2, 3, 4|]); let () = x |> Bigarray.Array1.get(_, 1); let () = x |> Bigarray.Array2.get(_, 1, 2); let () = x |> Bigarray.Array3.get(_, 1, 2, 3); let x=-.1; let x=-1; let x=+1; let x=+.1; let x = (~a: int=- 1) => a; let x = (~a=-1) => a; let x: float=-.1; let x: int =-1; let x: int=+1; let x: float =+. 1; foo(~a=?-1); /* https://github.com/facebook/reason/issues/1992 Pexp_override */ let z = {}; let z = {<>}; /* https://github.com/facebook/reason/issues/2056 */ type foo = ~a:bool=? => int; /* https://github.com/facebook/reason/issues/2070 */ f(~commit=!build); /* https://github.com/facebook/reason/issues/2032 */ let predicate = predicate === Functions.alwaysTrue1 ? defaultPredicate : (fun | None => false | Some(exn) => predicate(exn)); let predicate = predicate === Functions.alwaysTrue1 ? (fun | None => false | Some(exn) => predicate(exn)) : (fun | None => false | Some(exn) => predicate(exn)); /* https://github.com/facebook/reason/issues/2125 */ foo(~a); foo(~a: int); foo(~(a: int)); foo(~(a :> int)); foo(~a :> int); foo(~Foo.a?); foo(~Foo.a); /* https://github.com/facebook/reason/issues/2155#issuecomment-422077648 */ true ? (Update({...a, b: 1}), None) : x; true ? ({...a, b: 1}) : a; true ? (a, {...a, b: 1}) : a; true ? ([x, ...xs]) => f(x, xs) : a; /* https://github.com/facebook/reason/issues/2200 */ foo(~x=-1 + 2); foo(~x=-1 + 2: int); foo(~not); let foo = (~not) => (); let foo = (~not: string) => (); foo(~not: string); /* https://github.com/facebook/reason/issues/2141 */ let testCallNamedArgs = (foo: ((~a: int, ~b: int) => int), a, b) => foo(~a, ~b); let testCallNamedArgs = (foo: ((~a: int, ~b: int=?) => int), a, b) => foo(~a, ~b); let Foo.{name} = bar; let Foo.Bar.{name} = bar; let Foo.[ name ] = bar; let Foo.Bar.[ name ] = bar; let Foo.Bar.[] = bar; let Foo.Bar.[||] = bar; let Foo.() = foo; /* let Foo.(bar, baz) = foo; */ let Foo.(exception bar) = baz; try({ let this = try_exp; let has = hugged; parens; }) { | _ => () }; /* Pcl_open (4.06+) */ class x = { open EM; as self; }; class y = { open EM; open OM; as self; };