firefly-compiler 0.5.35 → 0.5.36
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.
- package/.hintrc +4 -4
- package/.vscode/settings.json +4 -4
- package/bin/Release.ff +157 -154
- package/bin/firefly.mjs +1 -1
- package/compiler/Builder.ff +275 -277
- package/compiler/Compiler.ff +234 -233
- package/compiler/Dependencies.ff +186 -187
- package/compiler/DependencyLock.ff +17 -17
- package/compiler/Deriver.ff +23 -31
- package/compiler/Dictionaries.ff +1 -1
- package/compiler/Inference.ff +43 -20
- package/compiler/JsEmitter.ff +1437 -1282
- package/compiler/LspHook.ff +202 -202
- package/compiler/Main.ff +25 -24
- package/compiler/ModuleCache.ff +178 -178
- package/compiler/Parser.ff +36 -109
- package/compiler/Resolver.ff +5 -8
- package/compiler/Substitution.ff +1 -1
- package/compiler/Syntax.ff +1 -16
- package/compiler/Token.ff +9 -0
- package/compiler/Tokenizer.ff +4 -0
- package/compiler/Workspace.ff +88 -88
- package/core/.firefly/include/package.json +5 -5
- package/core/.firefly/package.ff +2 -2
- package/core/Any.ff +26 -30
- package/core/Array.ff +298 -265
- package/core/Atomic.ff +63 -64
- package/core/Box.ff +7 -7
- package/core/BrowserSystem.ff +40 -40
- package/core/Buffer.ff +185 -152
- package/core/BuildSystem.ff +156 -148
- package/core/Channel.ff +95 -92
- package/core/Char.ff +3 -2
- package/core/Core.ff +16 -23
- package/core/Crypto.ff +94 -96
- package/core/Equal.ff +41 -36
- package/core/Error.ff +15 -10
- package/core/FileHandle.ff +45 -37
- package/core/Float.ff +176 -200
- package/core/HttpClient.ff +142 -148
- package/core/Instant.ff +6 -8
- package/core/Int.ff +40 -24
- package/core/IntMap.ff +61 -39
- package/core/Js.ff +305 -0
- package/core/JsSystem.ff +135 -114
- package/core/JsValue.ff +303 -159
- package/core/Json.ff +423 -443
- package/core/List.ff +482 -486
- package/core/Lock.ff +108 -144
- package/core/Log.ff +25 -14
- package/core/NodeSystem.ff +198 -191
- package/core/Ordering.ff +160 -161
- package/core/Path.ff +377 -409
- package/core/Queue.ff +90 -0
- package/core/Random.ff +140 -134
- package/core/RbMap.ff +216 -216
- package/core/Serializable.ff +16 -13
- package/core/Show.ff +44 -43
- package/core/SourceLocation.ff +68 -68
- package/core/Stream.ff +1 -1
- package/core/String.ff +224 -202
- package/core/StringMap.ff +58 -36
- package/core/Task.ff +165 -149
- package/experimental/benchmarks/ListGrab.ff +23 -23
- package/experimental/benchmarks/ListGrab.java +55 -55
- package/experimental/benchmarks/Pyrotek45.ff +30 -30
- package/experimental/benchmarks/Pyrotek45.java +64 -64
- package/experimental/bidirectional/Bidi.ff +88 -88
- package/experimental/lines/Main.ff +40 -0
- package/experimental/random/Index.ff +53 -53
- package/experimental/random/Process.ff +120 -120
- package/experimental/random/RunLength.ff +65 -65
- package/experimental/random/Scrape.ff +51 -51
- package/experimental/random/Symbols.ff +73 -73
- package/experimental/random/Tensor.ff +52 -52
- package/experimental/random/Units.ff +36 -36
- package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
- package/experimental/s3/S3TestPut.ff +16 -16
- package/experimental/tests/TestJson.ff +26 -26
- package/firefly.sh +0 -0
- package/fireflysite/.firefly/package.ff +4 -4
- package/fireflysite/CommunityOverview.ff +20 -20
- package/fireflysite/CountingButtonDemo.ff +58 -58
- package/fireflysite/DocumentParser.ff +325 -331
- package/fireflysite/ExamplesOverview.ff +40 -40
- package/fireflysite/FrontPage.ff +344 -344
- package/fireflysite/GettingStarted.ff +45 -45
- package/fireflysite/Guide.ff +456 -456
- package/fireflysite/Main.ff +163 -152
- package/fireflysite/MatchingPasswordsDemo.ff +82 -82
- package/fireflysite/PackagesOverview.ff +49 -49
- package/fireflysite/PostgresqlDemo.ff +34 -34
- package/fireflysite/ReferenceAll.ff +18 -18
- package/fireflysite/ReferenceIntroduction.ff +11 -11
- package/fireflysite/Styles.ff +567 -567
- package/fireflysite/Test.ff +121 -62
- package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
- package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
- package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
- package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
- package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -172
- package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
- package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
- package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
- package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
- package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
- package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
- package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
- package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
- package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
- package/lsp/.firefly/package.ff +1 -1
- package/lsp/CompletionHandler.ff +827 -827
- package/lsp/Handler.ff +714 -714
- package/lsp/HoverHandler.ff +79 -79
- package/lsp/LanguageServer.ff +272 -272
- package/lsp/SignatureHelpHandler.ff +55 -55
- package/lsp/SymbolHandler.ff +181 -181
- package/lsp/TestReferences.ff +17 -17
- package/lsp/TestReferencesCase.ff +7 -7
- package/lsp/stderr.txt +1 -1
- package/lsp/stdout.txt +34 -34
- package/lux/.firefly/package.ff +1 -1
- package/lux/Css.ff +648 -648
- package/lux/CssTest.ff +48 -48
- package/lux/Lux.ff +608 -617
- package/lux/LuxEvent.ff +79 -116
- package/lux/Main.ff +123 -123
- package/lux/Main2.ff +143 -143
- package/lux/TestDry.ff +28 -28
- package/output/js/ff/compiler/Builder.mjs +72 -71
- package/output/js/ff/compiler/Compiler.mjs +19 -13
- package/output/js/ff/compiler/Dependencies.mjs +8 -7
- package/output/js/ff/compiler/DependencyLock.mjs +6 -4
- package/output/js/ff/compiler/Deriver.mjs +26 -24
- package/output/js/ff/compiler/Dictionaries.mjs +14 -18
- package/output/js/ff/compiler/Environment.mjs +6 -4
- package/output/js/ff/compiler/Inference.mjs +238 -164
- package/output/js/ff/compiler/JsEmitter.mjs +1160 -350
- package/output/js/ff/compiler/JsImporter.mjs +20 -18
- package/output/js/ff/compiler/LspHook.mjs +12 -10
- package/output/js/ff/compiler/Main.mjs +61 -41
- package/output/js/ff/compiler/ModuleCache.mjs +10 -8
- package/output/js/ff/compiler/Parser.mjs +153 -669
- package/output/js/ff/compiler/Patterns.mjs +12 -10
- package/output/js/ff/compiler/Resolver.mjs +52 -78
- package/output/js/ff/compiler/Substitution.mjs +12 -16
- package/output/js/ff/compiler/Syntax.mjs +50 -341
- package/output/js/ff/compiler/Token.mjs +126 -4
- package/output/js/ff/compiler/Tokenizer.mjs +62 -52
- package/output/js/ff/compiler/Unification.mjs +74 -90
- package/output/js/ff/compiler/Wildcards.mjs +4 -2
- package/output/js/ff/compiler/Workspace.mjs +26 -20
- package/output/js/ff/core/Any.mjs +20 -20
- package/output/js/ff/core/Array.mjs +268 -175
- package/output/js/ff/core/AssetSystem.mjs +8 -6
- package/output/js/ff/core/Atomic.mjs +84 -52
- package/output/js/ff/core/Bool.mjs +6 -4
- package/output/js/ff/core/BrowserSystem.mjs +38 -29
- package/output/js/ff/core/Buffer.mjs +285 -133
- package/output/js/ff/core/BuildSystem.mjs +36 -56
- package/output/js/ff/core/Channel.mjs +250 -97
- package/output/js/ff/core/Char.mjs +5 -3
- package/output/js/ff/core/Core.mjs +28 -34
- package/output/js/ff/core/Crypto.mjs +30 -52
- package/output/js/ff/core/Duration.mjs +4 -2
- package/output/js/ff/core/Equal.mjs +14 -12
- package/output/js/ff/core/Error.mjs +17 -11
- package/output/js/ff/core/FileHandle.mjs +76 -38
- package/output/js/ff/core/Float.mjs +92 -160
- package/output/js/ff/core/HttpClient.mjs +208 -76
- package/output/js/ff/core/Instant.mjs +8 -10
- package/output/js/ff/core/Int.mjs +36 -26
- package/output/js/ff/core/IntMap.mjs +79 -33
- package/output/js/ff/core/Js.mjs +751 -0
- package/output/js/ff/core/JsSystem.mjs +54 -60
- package/output/js/ff/core/JsValue.mjs +294 -143
- package/output/js/ff/core/Json.mjs +443 -253
- package/output/js/ff/core/List.mjs +262 -214
- package/output/js/ff/core/Lock.mjs +156 -125
- package/output/js/ff/core/Log.mjs +20 -10
- package/output/js/ff/core/Map.mjs +10 -8
- package/output/js/ff/core/NodeSystem.mjs +189 -123
- package/output/js/ff/core/Nothing.mjs +4 -2
- package/output/js/ff/core/Option.mjs +40 -38
- package/output/js/ff/core/Ordering.mjs +26 -20
- package/output/js/ff/core/Pair.mjs +4 -2
- package/output/js/ff/core/Path.mjs +517 -315
- package/output/js/ff/core/Queue.mjs +306 -0
- package/output/js/ff/core/Random.mjs +141 -77
- package/output/js/ff/core/RbMap.mjs +36 -34
- package/output/js/ff/core/Serializable.mjs +44 -28
- package/output/js/ff/core/Set.mjs +6 -4
- package/output/js/ff/core/Show.mjs +8 -6
- package/output/js/ff/core/SourceLocation.mjs +4 -2
- package/output/js/ff/core/Stream.mjs +30 -50
- package/output/js/ff/core/String.mjs +263 -172
- package/output/js/ff/core/StringMap.mjs +77 -31
- package/output/js/ff/core/Task.mjs +91 -76
- package/output/js/ff/core/Try.mjs +20 -18
- package/output/js/ff/core/Unit.mjs +4 -2
- package/package.json +1 -1
- package/postgresql/Pg.ff +53 -59
- package/rpc/.firefly/package.ff +1 -1
- package/rpc/Rpc.ff +70 -70
- package/s3/.firefly/package.ff +1 -1
- package/s3/S3.ff +92 -94
- package/vscode/LICENSE.txt +21 -21
- package/vscode/Prepublish.ff +15 -15
- package/vscode/README.md +16 -16
- package/vscode/client/package-lock.json +544 -544
- package/vscode/client/package.json +22 -22
- package/vscode/client/src/extension.ts +104 -104
- package/vscode/icons/firefly-icon.svg +10 -10
- package/vscode/language-configuration.json +61 -61
- package/vscode/package-lock.json +3623 -3623
- package/vscode/package.json +1 -1
- package/vscode/snippets.json +241 -241
- package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
- package/webserver/.firefly/include/package.json +5 -5
- package/webserver/.firefly/package.ff +2 -2
- package/webserver/WebServer.ff +647 -685
- package/websocket/.firefly/package.ff +1 -1
- package/websocket/WebSocket.ff +100 -131
- package/core/UnsafeJs.ff +0 -42
- package/output/js/ff/core/UnsafeJs.mjs +0 -191
|
@@ -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
|
+
...
|