/* 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::int list? => 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, int [@lookAtThisAttribute] ); 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--\ */ 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 a => a; /* Done */ let blah a => a; /* Done (almost) */ let blah 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 a {blahBlah} => a; let blah a {blahBlah} => a; let 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 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 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: fun a b => a + b, addThreeNumbers: fun a b c => a + b + c, addThreeNumbersTupled: fun (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) => fun 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 = { method x = 0; method y = 0; }; /* This parses but doesn't type check class myClass init => object method x => init method 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::a b::b => a + b; type named = a::int => b::int => int; /*B*/ let namedAlias a::aa b::bb => aa + bb; let namedAlias a::aa b::bb => aa + bb; type namedAlias = a::int => b::int => int; /*C*/ let namedAnnot a::(a: int) b::(b: int) => 20; /*D*/ let namedAliasAnnot a::(aa: int) b::(bb: int) => 20; /*E*/ let myOptional a::a=? b::b=? () => 10; type named = a::int? => b::int? => unit => int; /*F*/ let optionalAlias a::aa=? b::bb=? () => 10; /*G*/ let optionalAnnot a::(a: int)=? b::(b: int)=? () => 10; /*H*/ let optionalAliasAnnot a::(aa: int)=? b::(bb: int)=? () => 10; /*I: */ let defOptional a::a=10 b::b=10 () => 10; type named = a::int? => b::int? => unit => int; /*J*/ let defOptionalAlias a::aa=10 b::bb=10 () => 10; /*K*/ let defOptionalAnnot a::(a: int)=10 b::(b: int)=10 () => 10; /*L*/ let defOptionalAliasAnnot a::(aa: int)=10 b::(bb: int)=10 () => 10; /*M: Invoking them - Punned */ let resNotAnnotated = named a::a b::b; /*N:*/ let resAnnotated: int = named a::a b::b; /*O: Invoking them */ let resNotAnnotated = named a::a b::b; /*P: Invoking them */ let resAnnotated: int = named a::a b::b; /*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: ty = named a::a b; /*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: int = myOptional a::?None b::?None; /*U: Explicitly passing optional with identifier expression */ let a = None; let explictlyPassed = myOptional a::?a b::?None; let explictlyPassedAnnotated: int = myOptional a::?a b::?None; 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 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: thing blah = aTypeAnnotation; let something: thing blah = thisIsANamedArg; let something: thing blah = aTypeAnnotation; let something: blah = thisIsANamedArg thing; let something: blah = typeAnnotation thing; 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;