firefly-compiler 0.5.39 → 0.5.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +158 -157
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +275 -275
  6. package/compiler/Compiler.ff +234 -234
  7. package/compiler/Dependencies.ff +186 -186
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/JsEmitter.ff +1437 -1437
  10. package/compiler/LspHook.ff +202 -202
  11. package/compiler/ModuleCache.ff +178 -178
  12. package/compiler/Workspace.ff +88 -88
  13. package/core/.firefly/include/package.json +5 -5
  14. package/core/.firefly/package.ff +2 -2
  15. package/core/Any.ff +25 -25
  16. package/core/Array.ff +298 -298
  17. package/core/Atomic.ff +63 -63
  18. package/core/Box.ff +7 -7
  19. package/core/BrowserSystem.ff +40 -40
  20. package/core/BuildSystem.ff +156 -156
  21. package/core/Crypto.ff +94 -94
  22. package/core/Equal.ff +41 -41
  23. package/core/Error.ff +25 -25
  24. package/core/HttpClient.ff +142 -142
  25. package/core/Instant.ff +24 -24
  26. package/core/Js.ff +305 -305
  27. package/core/JsSystem.ff +135 -135
  28. package/core/Json.ff +423 -423
  29. package/core/List.ff +482 -482
  30. package/core/Lock.ff +108 -108
  31. package/core/NodeSystem.ff +198 -198
  32. package/core/Ordering.ff +160 -160
  33. package/core/Path.ff +377 -378
  34. package/core/Queue.ff +90 -90
  35. package/core/Random.ff +140 -140
  36. package/core/RbMap.ff +216 -216
  37. package/core/Show.ff +44 -44
  38. package/core/SourceLocation.ff +68 -68
  39. package/core/Task.ff +165 -165
  40. package/experimental/benchmarks/ListGrab.ff +23 -23
  41. package/experimental/benchmarks/ListGrab.java +55 -55
  42. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  43. package/experimental/benchmarks/Pyrotek45.java +64 -64
  44. package/experimental/bidirectional/Bidi.ff +88 -88
  45. package/experimental/lines/Main.ff +40 -40
  46. package/experimental/random/Index.ff +53 -53
  47. package/experimental/random/Process.ff +120 -120
  48. package/experimental/random/RunLength.ff +65 -65
  49. package/experimental/random/Scrape.ff +51 -51
  50. package/experimental/random/Symbols.ff +73 -73
  51. package/experimental/random/Tensor.ff +52 -52
  52. package/experimental/random/Units.ff +36 -36
  53. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  54. package/experimental/s3/S3TestPut.ff +16 -16
  55. package/experimental/tests/TestJson.ff +26 -26
  56. package/firefly.sh +0 -0
  57. package/fireflysite/.firefly/package.ff +4 -4
  58. package/fireflysite/CommunityOverview.ff +20 -20
  59. package/fireflysite/CountingButtonDemo.ff +58 -58
  60. package/fireflysite/DocumentParser.ff +325 -325
  61. package/fireflysite/ExamplesOverview.ff +40 -40
  62. package/fireflysite/FrontPage.ff +344 -344
  63. package/fireflysite/GettingStarted.ff +45 -45
  64. package/fireflysite/Guide.ff +456 -456
  65. package/fireflysite/Main.ff +163 -163
  66. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  67. package/fireflysite/PackagesOverview.ff +49 -49
  68. package/fireflysite/PostgresqlDemo.ff +34 -34
  69. package/fireflysite/ReferenceAll.ff +18 -18
  70. package/fireflysite/ReferenceIntroduction.ff +11 -11
  71. package/fireflysite/Styles.ff +567 -567
  72. package/fireflysite/Test.ff +121 -121
  73. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
  74. package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
  75. package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
  76. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
  77. package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -235
  78. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
  79. package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
  80. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
  81. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
  82. package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
  83. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
  84. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
  85. package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
  86. package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
  87. package/lsp/.firefly/package.ff +1 -1
  88. package/lsp/CompletionHandler.ff +827 -827
  89. package/lsp/Handler.ff +714 -714
  90. package/lsp/HoverHandler.ff +79 -79
  91. package/lsp/LanguageServer.ff +272 -272
  92. package/lsp/SignatureHelpHandler.ff +55 -55
  93. package/lsp/SymbolHandler.ff +181 -181
  94. package/lsp/TestReferences.ff +17 -17
  95. package/lsp/TestReferencesCase.ff +7 -7
  96. package/lsp/stderr.txt +1 -1
  97. package/lsp/stdout.txt +34 -34
  98. package/lux/.firefly/package.ff +1 -1
  99. package/lux/Css.ff +648 -648
  100. package/lux/CssTest.ff +48 -48
  101. package/lux/Lux.ff +608 -608
  102. package/lux/LuxEvent.ff +79 -79
  103. package/lux/Main.ff +123 -123
  104. package/lux/Main2.ff +143 -143
  105. package/lux/TestDry.ff +28 -28
  106. package/output/js/ff/compiler/Builder.mjs +36 -36
  107. package/output/js/ff/core/Path.mjs +0 -2
  108. package/package.json +1 -1
  109. package/rpc/.firefly/package.ff +1 -1
  110. package/rpc/Rpc.ff +70 -70
  111. package/s3/.firefly/package.ff +1 -1
  112. package/s3/S3.ff +92 -92
  113. package/vscode/LICENSE.txt +21 -21
  114. package/vscode/Prepublish.ff +15 -15
  115. package/vscode/README.md +16 -16
  116. package/vscode/client/package-lock.json +544 -544
  117. package/vscode/client/package.json +22 -22
  118. package/vscode/client/src/extension.ts +104 -104
  119. package/vscode/icons/firefly-icon.svg +10 -10
  120. package/vscode/language-configuration.json +61 -61
  121. package/vscode/package-lock.json +3623 -3623
  122. package/vscode/package.json +1 -1
  123. package/vscode/snippets.json +241 -241
  124. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  125. package/webserver/.firefly/include/package.json +5 -5
  126. package/webserver/.firefly/package.ff +2 -2
  127. package/webserver/WebServer.ff +647 -647
  128. package/websocket/.firefly/package.ff +1 -1
  129. package/websocket/WebSocket.ff +100 -100
@@ -1,184 +1,184 @@
1
- # User defined types
2
-
3
- Named types can be defined at the top level, using one of four keywords: `data`, `class`, `capability` or `newtype`.
4
-
5
-
6
- # data
7
-
8
- To define an immutable type, you can use the `data` keyword.
9
-
10
- ```firefly
11
- data Shape {
12
- Circle(x: Float, y: Float, radius: Float)
13
- Rectangle(x: Float, y: Float, width: Float, height: Float)
14
- }
15
- ```
16
-
17
- This defines a type called `Shape` with two variants `Circle` and `Rectangle`.
18
- The `Circle` variant has three named fields of type `Float`, while the `Rectangle` variant has four.
19
-
20
- Type and variant names must start with a capital letter.
21
-
22
- No `class` or `capability` types can occur in the definition of a `data` type.
23
-
24
- A value of type `Shape` is either a `Circle` or a `Rectangle`, and they can be constructed as follows:
25
-
26
- ```firefly
27
- Circle(0.0, 0.0, 1.0) // Variant, : Shape
28
- Rectangle(5.0, 7.0, 3.0, 2.0) // Variant, : Shape
29
- ```
30
-
31
- Above the variants are constructed using positional arguments, whose order must coincide with the order of the parameters in the type definition.
32
-
33
- Named arguments are supported as well:
34
-
35
- ```firefly
36
- Circle(x = 0.0, y = 0.0, radius = 1.0)
37
- ```
38
-
39
- Named parameters don't have to be in order, and can be mixed with positional arguments:
40
-
41
- ```firefly
42
- Circle(radius = 1.0, 0.0, 0.0)
43
- ```
44
-
45
- To branch on the specific variant of a type, use [pattern matching](pattern-matching).
46
-
47
-
48
- # Common fields
49
-
50
- When all the variants share a set of fields, they can be moved to the common fields section of the declaration:
51
-
52
- ```firefly
53
- data Shape(x: Float, y: Float) {
54
- Circle(radius: Float)
55
- Rectangle(width: Float, height: Float)
56
- }
57
- ```
58
-
59
- This is similar to the `Shape` definition above, but the `x` and `y` fields have been pulled out as common fields.
60
- Given a value of a type, e.g. `shape: Shape`, common fields can be accessed without knowing which specific variant it is:
61
-
62
- ```firefly
63
- shape.x // Returns a Float
64
- shape.y // Returns a Float
65
- ```
66
-
67
- When there is only one variant of a type, and its name coincides with the name of the type, we can use a shorthand definition:
68
-
69
- ```firefly
70
- data Point(x: Float, y: Float)
71
- ```
72
-
73
- This defines a type called `Point` with a single variant, also named `Point`, and two common fields of type `Float`.
74
-
75
-
76
- # Copying
77
-
78
- If you have a value and want to construct a variant, you can copy each field explicitly:
79
-
80
- ```firefly
81
- Rectangle(x = point.x, y = point.y, width = 2.0, height = 1.5)
82
- ```
83
-
84
- There's a shorthand for doing this, however:
85
-
86
- ```firefly
87
- point.Rectangle(width = 2.0, height = 1.5)
88
- ```
89
-
90
- Using this shorthand, the fields of `Rectangle` that aren't specified will be copied from `point`.
91
-
92
-
93
-
94
- # class
95
-
96
- Types defined with the `class` keyword work like `data` types, except for the differences noted here.
97
-
98
- Fields of `class` types may be declared `mutable`:
99
-
100
- ```firefly
101
- class FruitBasket(
102
- mutable apples: Int
103
- mutable oranges: Int
104
- mutable bananas: Int
105
- )
106
- ```
107
-
108
- Mutable fields can be updated. Given a value `basket: FruitBasket`, its fields can be assigned to:
109
-
110
- ```firefly
111
- basket.apples = 42 // The apples field now holds the value 42
112
- basket.oranges += 1 // The oranges field is now one greater
113
- basket.bananas -= 1 // The bananas field is now one less
114
- ```
115
-
116
- Except for `capability` types, all types can occur in the definition of a `class` type.
117
-
118
-
119
- # capability
120
-
121
- Types defined with the `capability` keyword work like `class` types, except for the differences noted here.
122
-
123
- ```firefly
124
- capability EventHandler(
125
- onEvent: () => Unit
126
- )
127
- ```
128
-
129
- The `onEvent` field here contains a first class function, which may have captured other capabilities or classes in its closure.
130
- Therefore, calling the function contained in this field may cause side effects.
131
-
132
- In particular, it may have captured the `system` argument that's passed to the main function, or other capabilities that allow it to do I/O.
133
-
134
- There are no restrictions on the types of fields that `capability` types can have.
135
-
136
- Function types `=>` are considered `capability` types.
137
-
138
-
139
- # newtype
140
-
141
- Types defined with the `newtype` keyword work like `data` types, except that they must have exactly one common field and no explicitly listed variants.
142
-
143
- ```firefly
144
- newtype UserId(id: Int)
145
- ```
146
-
147
- At runtime, they are represented as values of the field type.
148
- In this case, it means that `UserId(42)` is represented as the `Int` value `42` at runtime, with zero overhead.
149
-
150
-
151
- # Generic types
152
-
153
- Whether you use `data`, `class`, `capability` or `newtype` to define a type, it may have type parameters.
154
-
155
- ```firefly
156
- data Basket[T](
157
- items: List[T]
158
- )
159
- ```
160
-
161
- Here the `T` in `Basket[T]` is a type parameter, and it's used as a type argument in `List[T]`.
162
-
163
- An unbounded type parameter can be instantiated to any type.
164
- We can have a `Basket[Shape]`, which has a field `items: List[Shape]`, and a different type `Basket[EventHandler]`, which has a field `items: List[EventHandler]`.
165
-
166
- Note that type parameters are not concrete types, and are thus not subject to the field type restrictions stated earlier.
167
-
168
-
169
- # Anonymous records
170
-
171
- An anonymous record is not defined anywhere, but consists of zero or more fields. The fields may have any type, but can't be reassigned after creation.
172
-
173
- ```firefly
174
- (red = 255, green = 255, blue = 0)
175
- ```
176
-
177
- This constructs an anonymous record.
178
- If you have an anonymous record value, e.g. `color: (red: Int, green: Int, blue: Int)`, you can access its fields:
179
-
180
- ```firefly
181
- color.red // Returns an Int
182
- color.green // Returns an Int
183
- color.blue // Returns an Int
184
- ```
1
+ # User defined types
2
+
3
+ Named types can be defined at the top level, using one of four keywords: `data`, `class`, `capability` or `newtype`.
4
+
5
+
6
+ # data
7
+
8
+ To define an immutable type, you can use the `data` keyword.
9
+
10
+ ```firefly
11
+ data Shape {
12
+ Circle(x: Float, y: Float, radius: Float)
13
+ Rectangle(x: Float, y: Float, width: Float, height: Float)
14
+ }
15
+ ```
16
+
17
+ This defines a type called `Shape` with two variants `Circle` and `Rectangle`.
18
+ The `Circle` variant has three named fields of type `Float`, while the `Rectangle` variant has four.
19
+
20
+ Type and variant names must start with a capital letter.
21
+
22
+ No `class` or `capability` types can occur in the definition of a `data` type.
23
+
24
+ A value of type `Shape` is either a `Circle` or a `Rectangle`, and they can be constructed as follows:
25
+
26
+ ```firefly
27
+ Circle(0.0, 0.0, 1.0) // Variant, : Shape
28
+ Rectangle(5.0, 7.0, 3.0, 2.0) // Variant, : Shape
29
+ ```
30
+
31
+ Above the variants are constructed using positional arguments, whose order must coincide with the order of the parameters in the type definition.
32
+
33
+ Named arguments are supported as well:
34
+
35
+ ```firefly
36
+ Circle(x = 0.0, y = 0.0, radius = 1.0)
37
+ ```
38
+
39
+ Named parameters don't have to be in order, and can be mixed with positional arguments:
40
+
41
+ ```firefly
42
+ Circle(radius = 1.0, 0.0, 0.0)
43
+ ```
44
+
45
+ To branch on the specific variant of a type, use [pattern matching](pattern-matching).
46
+
47
+
48
+ # Common fields
49
+
50
+ When all the variants share a set of fields, they can be moved to the common fields section of the declaration:
51
+
52
+ ```firefly
53
+ data Shape(x: Float, y: Float) {
54
+ Circle(radius: Float)
55
+ Rectangle(width: Float, height: Float)
56
+ }
57
+ ```
58
+
59
+ This is similar to the `Shape` definition above, but the `x` and `y` fields have been pulled out as common fields.
60
+ Given a value of a type, e.g. `shape: Shape`, common fields can be accessed without knowing which specific variant it is:
61
+
62
+ ```firefly
63
+ shape.x // Returns a Float
64
+ shape.y // Returns a Float
65
+ ```
66
+
67
+ When there is only one variant of a type, and its name coincides with the name of the type, we can use a shorthand definition:
68
+
69
+ ```firefly
70
+ data Point(x: Float, y: Float)
71
+ ```
72
+
73
+ This defines a type called `Point` with a single variant, also named `Point`, and two common fields of type `Float`.
74
+
75
+
76
+ # Copying
77
+
78
+ If you have a value and want to construct a variant, you can copy each field explicitly:
79
+
80
+ ```firefly
81
+ Rectangle(x = point.x, y = point.y, width = 2.0, height = 1.5)
82
+ ```
83
+
84
+ There's a shorthand for doing this, however:
85
+
86
+ ```firefly
87
+ point.Rectangle(width = 2.0, height = 1.5)
88
+ ```
89
+
90
+ Using this shorthand, the fields of `Rectangle` that aren't specified will be copied from `point`.
91
+
92
+
93
+
94
+ # class
95
+
96
+ Types defined with the `class` keyword work like `data` types, except for the differences noted here.
97
+
98
+ Fields of `class` types may be declared `mutable`:
99
+
100
+ ```firefly
101
+ class FruitBasket(
102
+ mutable apples: Int
103
+ mutable oranges: Int
104
+ mutable bananas: Int
105
+ )
106
+ ```
107
+
108
+ Mutable fields can be updated. Given a value `basket: FruitBasket`, its fields can be assigned to:
109
+
110
+ ```firefly
111
+ basket.apples = 42 // The apples field now holds the value 42
112
+ basket.oranges += 1 // The oranges field is now one greater
113
+ basket.bananas -= 1 // The bananas field is now one less
114
+ ```
115
+
116
+ Except for `capability` types, all types can occur in the definition of a `class` type.
117
+
118
+
119
+ # capability
120
+
121
+ Types defined with the `capability` keyword work like `class` types, except for the differences noted here.
122
+
123
+ ```firefly
124
+ capability EventHandler(
125
+ onEvent: () => Unit
126
+ )
127
+ ```
128
+
129
+ The `onEvent` field here contains a first class function, which may have captured other capabilities or classes in its closure.
130
+ Therefore, calling the function contained in this field may cause side effects.
131
+
132
+ In particular, it may have captured the `system` argument that's passed to the main function, or other capabilities that allow it to do I/O.
133
+
134
+ There are no restrictions on the types of fields that `capability` types can have.
135
+
136
+ Function types `=>` are considered `capability` types.
137
+
138
+
139
+ # newtype
140
+
141
+ Types defined with the `newtype` keyword work like `data` types, except that they must have exactly one common field and no explicitly listed variants.
142
+
143
+ ```firefly
144
+ newtype UserId(id: Int)
145
+ ```
146
+
147
+ At runtime, they are represented as values of the field type.
148
+ In this case, it means that `UserId(42)` is represented as the `Int` value `42` at runtime, with zero overhead.
149
+
150
+
151
+ # Generic types
152
+
153
+ Whether you use `data`, `class`, `capability` or `newtype` to define a type, it may have type parameters.
154
+
155
+ ```firefly
156
+ data Basket[T](
157
+ items: List[T]
158
+ )
159
+ ```
160
+
161
+ Here the `T` in `Basket[T]` is a type parameter, and it's used as a type argument in `List[T]`.
162
+
163
+ An unbounded type parameter can be instantiated to any type.
164
+ We can have a `Basket[Shape]`, which has a field `items: List[Shape]`, and a different type `Basket[EventHandler]`, which has a field `items: List[EventHandler]`.
165
+
166
+ Note that type parameters are not concrete types, and are thus not subject to the field type restrictions stated earlier.
167
+
168
+
169
+ # Anonymous records
170
+
171
+ An anonymous record is not defined anywhere, but consists of zero or more fields. The fields may have any type, but can't be reassigned after creation.
172
+
173
+ ```firefly
174
+ (red = 255, green = 255, blue = 0)
175
+ ```
176
+
177
+ This constructs an anonymous record.
178
+ If you have an anonymous record value, e.g. `color: (red: Int, green: Int, blue: Int)`, you can access its fields:
179
+
180
+ ```firefly
181
+ color.red // Returns an Int
182
+ color.green // Returns an Int
183
+ color.blue // Returns an Int
184
+ ```
@@ -1,136 +1,136 @@
1
- # Control flow
2
-
3
- Firefly provides several ways to implement branching. Pattern matching is the most powerful, built directly into the language. `if`, `elseIf` and `else` do what you expect and are functions and methods in the standard library, implemented using the `Option` type. An then finally, there are [exceptions](#Exceptions).
4
-
5
- # Pattern matching
6
-
7
- Pattern matching allows you to check a given data structure against a pattern. For example, if we want to parse the command line arguments provided by the user, we could do it like this:
8
-
9
- ```firefly
10
- let pair = system.arguments().{
11
- | [host] => Pair(host, 80)
12
- | ["localhost", port] => Pair("localhost", port.grabInt())
13
- | _ =>
14
- system.writeErrorLine("Usage: 'localhost' | (host port)")
15
- system.exit(0)
16
- }
17
- ```
18
-
19
- In Firefly you construct a list of strings (`List[String]`) like this `["example.com", "80"]`, and using pattern matching you can de-construct in the same way.
20
-
21
- ```firefly
22
- data.{
23
- | pattern1 => // case 1
24
- | pattern2 => // case N
25
- ...
26
- }
27
- ```
28
-
29
- The patterns must be exhaustive, that is, for any possible value of the given type, there must be a matching pattern. In the example above, there are no value for the empty list `[]` , list with two values, where the first is not `"localhost"` or lists with more than 2 arguments. That's why we need the wildcard case at the end. Without this last case, the compiler would produce a compile-time error, stating that the patterns must be exhaustive.
30
-
31
- Here are more examples — all exhaustive. Let's start with records:
32
-
33
- ```firefly
34
- pair.{
35
- | Pair(first, second) =>
36
- }
37
- ```
38
-
39
- Numbers
40
-
41
- ```firefly
42
- n.{
43
- | 1 =>
44
- | 2 =>
45
- | n =>
46
- }
47
- ```
48
-
49
- Booleans
50
-
51
- ```firefly
52
- n.{
53
- | True =>
54
- | False =>
55
- }
56
- ```
57
-
58
- And you can combine pattern as needed. Imagine you have a pair of type `Pair[List[Bool], Pair(Int, String)]`
59
-
60
-
61
- ```firefly
62
- pair.{
63
- | Pair([True, False], Pair(42, "foo")) =>
64
- | other =>
65
- }
66
- ```
67
-
68
-
69
- # Option
70
-
71
- Sometimes you don't have a value. Other languages uses null for this purpose, but Firefly does not have null. Instead, we have `Option` from the core package.
72
-
73
-
74
- ```firefly
75
- data Option[T] {
76
- None
77
- Some(value: T)
78
- }
79
- ```
80
-
81
- For some type `T`, say `String`, `Option[String]` is either some string or no value `None`. This way, the type system guides you to check for no-value.
82
-
83
- Many functions and methods returns an `Option` in Firefly. For instance the `getInt` method on `String`. This method returns `Some[Int]` when the string consists only of digits and `None` otherwise. We can perform pattern matching on Option like this:
84
-
85
- ```firefly
86
- port.getInt().{
87
- | None => 80
88
- | Some(p) => p
89
- }
90
- ```
91
-
92
- Many methods like `getInt` have a non-total counterpart `grabInt`, which returns an `Int`. But it will throw an exception when the input cannot be parsed. Options let's you code in an exception-safe manner.
93
-
94
-
95
- # if - elseIf - else
96
-
97
- You write if-statements in Firefly like this:
98
-
99
- ```firefly
100
- if(path == "/") {
101
- response.writeText("<!doctype html>")
102
- } elseIf {path.startsWith("/js/")} {
103
- response.writeText("<script>")
104
- } else {
105
- response.writeStatus("404 Not found")
106
- }
107
- ```
108
-
109
- You can also use it as an expression like this
110
-
111
-
112
- ```firefly
113
- let contentType = if(path == "/") {
114
- "text/html; charset=UTF-8"
115
- } elseIf(directory2.exists) {
116
- "text/javascript; charset=UTF-8"
117
- } else {
118
- "text/plain; charset=UTF-8"
119
- }
120
- ```
121
-
122
- `if`, `elseIf` and `else` are not keywords or construct build into Firefly. `if` is just a function defined like this:
123
-
124
-
125
- ```firefly
126
- if[T](condition: Bool, body: () => T): Option[T] {
127
- condition.{
128
- | False => None
129
- | True => Some(body())
130
- }
131
- }
132
- ```
133
-
134
- # Exceptions
135
-
136
- ...
1
+ # Control flow
2
+
3
+ Firefly provides several ways to implement branching. Pattern matching is the most powerful, built directly into the language. `if`, `elseIf` and `else` do what you expect and are functions and methods in the standard library, implemented using the `Option` type. An then finally, there are [exceptions](#Exceptions).
4
+
5
+ # Pattern matching
6
+
7
+ Pattern matching allows you to check a given data structure against a pattern. For example, if we want to parse the command line arguments provided by the user, we could do it like this:
8
+
9
+ ```firefly
10
+ let pair = system.arguments().{
11
+ | [host] => Pair(host, 80)
12
+ | ["localhost", port] => Pair("localhost", port.grabInt())
13
+ | _ =>
14
+ system.writeErrorLine("Usage: 'localhost' | (host port)")
15
+ system.exit(0)
16
+ }
17
+ ```
18
+
19
+ In Firefly you construct a list of strings (`List[String]`) like this `["example.com", "80"]`, and using pattern matching you can de-construct in the same way.
20
+
21
+ ```firefly
22
+ data.{
23
+ | pattern1 => // case 1
24
+ | pattern2 => // case N
25
+ ...
26
+ }
27
+ ```
28
+
29
+ The patterns must be exhaustive, that is, for any possible value of the given type, there must be a matching pattern. In the example above, there are no value for the empty list `[]` , list with two values, where the first is not `"localhost"` or lists with more than 2 arguments. That's why we need the wildcard case at the end. Without this last case, the compiler would produce a compile-time error, stating that the patterns must be exhaustive.
30
+
31
+ Here are more examples — all exhaustive. Let's start with records:
32
+
33
+ ```firefly
34
+ pair.{
35
+ | Pair(first, second) =>
36
+ }
37
+ ```
38
+
39
+ Numbers
40
+
41
+ ```firefly
42
+ n.{
43
+ | 1 =>
44
+ | 2 =>
45
+ | n =>
46
+ }
47
+ ```
48
+
49
+ Booleans
50
+
51
+ ```firefly
52
+ n.{
53
+ | True =>
54
+ | False =>
55
+ }
56
+ ```
57
+
58
+ And you can combine pattern as needed. Imagine you have a pair of type `Pair[List[Bool], Pair(Int, String)]`
59
+
60
+
61
+ ```firefly
62
+ pair.{
63
+ | Pair([True, False], Pair(42, "foo")) =>
64
+ | other =>
65
+ }
66
+ ```
67
+
68
+
69
+ # Option
70
+
71
+ Sometimes you don't have a value. Other languages uses null for this purpose, but Firefly does not have null. Instead, we have `Option` from the core package.
72
+
73
+
74
+ ```firefly
75
+ data Option[T] {
76
+ None
77
+ Some(value: T)
78
+ }
79
+ ```
80
+
81
+ For some type `T`, say `String`, `Option[String]` is either some string or no value `None`. This way, the type system guides you to check for no-value.
82
+
83
+ Many functions and methods returns an `Option` in Firefly. For instance the `getInt` method on `String`. This method returns `Some[Int]` when the string consists only of digits and `None` otherwise. We can perform pattern matching on Option like this:
84
+
85
+ ```firefly
86
+ port.getInt().{
87
+ | None => 80
88
+ | Some(p) => p
89
+ }
90
+ ```
91
+
92
+ Many methods like `getInt` have a non-total counterpart `grabInt`, which returns an `Int`. But it will throw an exception when the input cannot be parsed. Options let's you code in an exception-safe manner.
93
+
94
+
95
+ # if - elseIf - else
96
+
97
+ You write if-statements in Firefly like this:
98
+
99
+ ```firefly
100
+ if(path == "/") {
101
+ response.writeText("<!doctype html>")
102
+ } elseIf {path.startsWith("/js/")} {
103
+ response.writeText("<script>")
104
+ } else {
105
+ response.writeStatus("404 Not found")
106
+ }
107
+ ```
108
+
109
+ You can also use it as an expression like this
110
+
111
+
112
+ ```firefly
113
+ let contentType = if(path == "/") {
114
+ "text/html; charset=UTF-8"
115
+ } elseIf(directory2.exists) {
116
+ "text/javascript; charset=UTF-8"
117
+ } else {
118
+ "text/plain; charset=UTF-8"
119
+ }
120
+ ```
121
+
122
+ `if`, `elseIf` and `else` are not keywords or construct build into Firefly. `if` is just a function defined like this:
123
+
124
+
125
+ ```firefly
126
+ if[T](condition: Bool, body: () => T): Option[T] {
127
+ condition.{
128
+ | False => None
129
+ | True => Some(body())
130
+ }
131
+ }
132
+ ```
133
+
134
+ # Exceptions
135
+
136
+ ...