firefly-compiler 0.4.79 → 0.4.80
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 +153 -153
- package/bin/firefly.mjs +1 -1
- package/compiler/Builder.ff +257 -257
- package/compiler/Compiler.ff +227 -227
- package/compiler/Dependencies.ff +187 -187
- package/compiler/DependencyLock.ff +17 -17
- package/compiler/Inference.ff +2 -1
- package/compiler/JsEmitter.ff +940 -946
- package/compiler/LspHook.ff +202 -202
- package/compiler/Main.ff +3 -3
- package/compiler/ModuleCache.ff +178 -178
- package/compiler/Tokenizer.ff +1 -1
- package/compiler/Unification.ff +1 -1
- package/compiler/Workspace.ff +88 -88
- package/core/.firefly/include/package-lock.json +564 -564
- package/core/.firefly/include/package.json +5 -5
- package/core/.firefly/include/prepare.sh +1 -1
- package/core/.firefly/package.ff +2 -2
- package/core/Array.ff +265 -265
- package/core/Atomic.ff +64 -64
- package/core/Box.ff +7 -7
- package/core/BrowserSystem.ff +40 -40
- package/core/BuildSystem.ff +148 -148
- package/core/Crypto.ff +96 -96
- package/core/Equal.ff +36 -36
- package/core/Float.ff +25 -0
- package/core/HttpClient.ff +148 -148
- package/core/JsSystem.ff +69 -69
- package/core/Json.ff +434 -434
- package/core/List.ff +486 -486
- package/core/Lock.ff +144 -144
- package/core/NodeSystem.ff +216 -216
- package/core/Ordering.ff +161 -161
- package/core/Path.ff +401 -401
- package/core/Random.ff +134 -134
- package/core/RbMap.ff +216 -216
- package/core/Show.ff +43 -43
- package/core/SourceLocation.ff +68 -68
- package/core/Stream.ff +9 -9
- package/core/Task.ff +141 -141
- package/core/Try.ff +25 -4
- 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/random/Index.ff +53 -53
- package/experimental/random/Process.ff +120 -120
- 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 +331 -217
- package/fireflysite/ExamplesOverview.ff +40 -40
- package/fireflysite/FrontPage.ff +344 -360
- package/fireflysite/{GuideIntroduction.ff → GettingStarted.ff} +45 -52
- package/fireflysite/Guide.ff +443 -411
- package/fireflysite/Main.ff +141 -137
- package/fireflysite/MatchingPasswordsDemo.ff +82 -82
- package/fireflysite/PackagesOverview.ff +49 -49
- package/fireflysite/PostgresqlDemo.ff +34 -34
- package/fireflysite/ReferenceAll.ff +19 -0
- package/fireflysite/ReferenceIntroduction.ff +11 -0
- package/fireflysite/Styles.ff +567 -495
- package/fireflysite/Test.ff +38 -0
- package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -0
- package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +208 -0
- package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +168 -0
- package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -0
- package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -0
- package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -0
- package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -0
- package/fireflysite/assets/markdown/{ControlFlow.md → scratch/ControlFlow.md} +136 -136
- package/fireflysite/assets/markdown/scratch/Toc.md +41 -0
- package/lsp/.firefly/package.ff +1 -1
- package/lsp/CompletionHandler.ff +828 -828
- 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 +487 -487
- package/lux/LuxEvent.ff +116 -116
- package/lux/Main.ff +123 -123
- package/lux/Main2.ff +143 -143
- package/output/js/ff/compiler/Builder.mjs +47 -47
- package/output/js/ff/compiler/Dependencies.mjs +3 -3
- package/output/js/ff/compiler/Inference.mjs +2 -2
- package/output/js/ff/compiler/JsEmitter.mjs +18 -72
- package/output/js/ff/compiler/Main.mjs +4 -4
- package/output/js/ff/compiler/ModuleCache.mjs +4 -4
- package/output/js/ff/core/Array.mjs +59 -59
- package/output/js/ff/core/Atomic.mjs +36 -36
- package/output/js/ff/core/BrowserSystem.mjs +11 -11
- package/output/js/ff/core/BuildSystem.mjs +30 -30
- package/output/js/ff/core/Crypto.mjs +40 -40
- package/output/js/ff/core/Float.mjs +50 -0
- package/output/js/ff/core/HttpClient.mjs +56 -56
- package/output/js/ff/core/Json.mjs +147 -147
- package/output/js/ff/core/List.mjs +50 -50
- package/output/js/ff/core/Lock.mjs +97 -97
- package/output/js/ff/core/NodeSystem.mjs +87 -87
- package/output/js/ff/core/Ordering.mjs +8 -8
- package/output/js/ff/core/Path.mjs +231 -231
- package/output/js/ff/core/Random.mjs +56 -56
- package/output/js/ff/core/Task.mjs +39 -39
- package/output/js/ff/core/Try.mjs +98 -4
- package/package.json +1 -1
- package/postgresql/Pg.ff +1 -1
- package/rpc/.firefly/package.ff +1 -1
- package/rpc/Rpc.ff +70 -70
- package/s3/.firefly/package.ff +1 -1
- package/s3/S3.ff +94 -94
- package/unsafejs/UnsafeJs.ff +19 -19
- package/vscode/LICENSE.txt +21 -21
- package/vscode/Prepublish.ff +15 -15
- package/vscode/README.md +16 -16
- 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-lock.json +22 -22
- package/webserver/.firefly/include/package.json +5 -5
- package/webserver/.firefly/package.ff +2 -2
- package/webserver/WebServer.ff +685 -685
- package/websocket/.firefly/package.ff +1 -1
- package/websocket/WebSocket.ff +131 -131
- package/fireflysite/GuideAll.ff +0 -21
- package/fireflysite/GuideBaseTypes.ff +0 -168
- package/fireflysite/GuideControlFlow.ff +0 -212
- package/fireflysite/assets/markdown/Example.md +0 -78
- /package/fireflysite/assets/{NotoSansMono-Regular.ttf → font/NotoSansMono-Regular.ttf} +0 -0
- /package/fireflysite/assets/{NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf → font/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf} +0 -0
- /package/fireflysite/assets/{autocomplete-small.png → image/autocomplete-small.png} +0 -0
- /package/fireflysite/assets/{autocomplete.png → image/autocomplete.png} +0 -0
- /package/fireflysite/assets/{edit-time-error.png → image/edit-time-error.png} +0 -0
- /package/fireflysite/assets/{firefly-logo-notext.png → image/firefly-logo-notext.png} +0 -0
- /package/fireflysite/assets/{firefly-logo-yellow.png → image/firefly-logo-yellow.png} +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import WebServer from ff:webserver // This is required to run the file.
|
|
2
|
+
|
|
3
|
+
nodeMain(system: NodeSystem) {
|
|
4
|
+
let f = {x, y => Pair(x, y)}
|
|
5
|
+
let g = {Pair(_, _)}
|
|
6
|
+
Log.show(f(1, 2))
|
|
7
|
+
Log.show(g(1, 2))
|
|
8
|
+
Log.show(factorial(5))
|
|
9
|
+
Log.show(factorialTail(5))
|
|
10
|
+
Log.show([1, 2].map({x => x + x}))
|
|
11
|
+
|
|
12
|
+
let x = {a, b => Pair(a, b)}(1, 2)
|
|
13
|
+
let f0: () => Unit = {42}
|
|
14
|
+
let p = {Pair(1, _)}
|
|
15
|
+
let p2 = {Pair(_, _)}
|
|
16
|
+
|
|
17
|
+
let increment: Int => Int = {i => i + 1}
|
|
18
|
+
let plus: (Int, Int) => Int = {a, b => a + b}
|
|
19
|
+
|
|
20
|
+
Log.show([1, 2].map(increment))
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
factorial(n: Int): Int {
|
|
25
|
+
if(n == 0) {
|
|
26
|
+
1
|
|
27
|
+
} else {
|
|
28
|
+
n * factorial(n - 1)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
factorialTail(n: Int, acc: Int = 1): Int {
|
|
33
|
+
if(n == 0) {
|
|
34
|
+
acc
|
|
35
|
+
} else {
|
|
36
|
+
tailcall factorialTail(n - 1, n * acc)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Base types
|
|
2
|
+
|
|
3
|
+
In Firefly, all named types are defined in a `.ff` file somewhere.
|
|
4
|
+
However, some of the types in the `ff:core` package have dedicated syntax.
|
|
5
|
+
These are considered base types and are documented in the following sections.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Bool
|
|
9
|
+
|
|
10
|
+
Values of the `Bool` type represent truth values. They are either `False` or `True`.
|
|
11
|
+
|
|
12
|
+
The type is defined in `ff:core` as follows:
|
|
13
|
+
|
|
14
|
+
```firefly
|
|
15
|
+
data Bool {
|
|
16
|
+
False
|
|
17
|
+
True
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Logical negation is an operator on booleans:
|
|
22
|
+
|
|
23
|
+
```firefly
|
|
24
|
+
!True == False
|
|
25
|
+
!False == True
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Logical and is an operator on booleans. The right hand side is only evaluated when the left hand side is `True`:
|
|
29
|
+
|
|
30
|
+
```firefly
|
|
31
|
+
True && True == True
|
|
32
|
+
True && False == False
|
|
33
|
+
False && True == False
|
|
34
|
+
False && False == False
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Logical or is an operator on booleans. The right hand side is only evaluated when the left hand side is `False`:
|
|
38
|
+
|
|
39
|
+
```firefly
|
|
40
|
+
True || True == True
|
|
41
|
+
True || False == True
|
|
42
|
+
False || True == True
|
|
43
|
+
False || False == False
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The comparison operators are supported for `Bool`:
|
|
47
|
+
|
|
48
|
+
```firefly
|
|
49
|
+
True == True // Equality
|
|
50
|
+
True != False // Inequality
|
|
51
|
+
False < True // Less than
|
|
52
|
+
False <= True // Less than or equal
|
|
53
|
+
True > False // Greater than
|
|
54
|
+
True >= True // Greater than or equal
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In addition, the standard library defines `if`, `while`, etc. as a functions with a `Bool` condition.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Int
|
|
61
|
+
|
|
62
|
+
Values of the `Int` type represent whole numbers in the range `-9,007,199,254,740,991` to `9,007,199,254,740,991`. This range is specifically chosen to work inside the safe integer range of the `Number` type in JavaScript. Outside of this range, `Int` values may act like `Float` values.
|
|
63
|
+
|
|
64
|
+
They can be constructed using the following literal syntax:
|
|
65
|
+
|
|
66
|
+
```firefly
|
|
67
|
+
42 // Fourty two
|
|
68
|
+
0 // Zero
|
|
69
|
+
-1 // Minus one
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Basic arithmetic operators are supported:
|
|
73
|
+
|
|
74
|
+
```firefly
|
|
75
|
+
3 + 5 // Addition, == 8
|
|
76
|
+
3 - 5 // Subtraction, == -2
|
|
77
|
+
3 * 5 // Multiplication, == 15
|
|
78
|
+
-(3 + 5) // Negation, == -8
|
|
79
|
+
3 / 5 // Division, == 0.6
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
As are the comparison operators.
|
|
83
|
+
|
|
84
|
+
Note that the division `3 / 5` does not return an `Int`, but rather a `Float` value `0.6`. If you want integer division, which rounds towards zero, you can do `3.div(5) == 0`.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Float
|
|
88
|
+
|
|
89
|
+
Values of the `Float` type are floating point numbers with the semantics of the `Number` type in JavaScript.
|
|
90
|
+
|
|
91
|
+
They be constructed using following literal syntax:
|
|
92
|
+
|
|
93
|
+
```firefly
|
|
94
|
+
42.0 // Fourty two point zero
|
|
95
|
+
0.0 // Zero point zero
|
|
96
|
+
-1.0 // Minus one point zero
|
|
97
|
+
1.0e3 // == 1000.0
|
|
98
|
+
1.0e-3 // == 0.001
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Like `Number` values in JavaScript, `Float` values may also be `Float.nan()`, `Float.infinity()` or `-Float.infinity()`.
|
|
102
|
+
|
|
103
|
+
Basic arithmetic operators are supported:
|
|
104
|
+
|
|
105
|
+
```firefly
|
|
106
|
+
3.5 + 5.75 // Addition, == 9.25
|
|
107
|
+
3.0 - 5.0 // Subtraction, == -2.0
|
|
108
|
+
3.0 * 5.0 // Multiplication, == 15.0
|
|
109
|
+
-(3.0 + 5.0) // Negation, == -8.0
|
|
110
|
+
3.0 / 5.0 // Division, == 0.6
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
As are the comparison operators.
|
|
114
|
+
However, as with `Number` values in JavaScript, comparing `Float.nan()` with any floating point value, including `Float.nan()` will return `False`.
|
|
115
|
+
To test whether a number is `Float.nan()`, use the `isNan()` method, e.g. `x.isNan()`.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Char
|
|
119
|
+
|
|
120
|
+
Values of the `Char` type represent UTF-16 code units.
|
|
121
|
+
Graphemes outside of the Unicode Basic Multilingual Plane may consist of multiple UTF-16 code units and thus require multiple `Char` values.
|
|
122
|
+
|
|
123
|
+
They can be constructed using following literal syntax, where `\` (backslash) is used to escape characters:
|
|
124
|
+
|
|
125
|
+
```firefly
|
|
126
|
+
' ' // A space
|
|
127
|
+
'\'' // Single quote
|
|
128
|
+
'\r' // Carriage return
|
|
129
|
+
'\n' // Line feed (Unix newline)
|
|
130
|
+
'\t' // Horizontal tab
|
|
131
|
+
'\\' // Backslash
|
|
132
|
+
'A' // Capital A
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The comparison operators also works for `Char` values, but note that they compare the code unit rather than compare by any language-specific character ordering.
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# String
|
|
139
|
+
|
|
140
|
+
Values of the `String` type are immutable sequences of `Char` values, representing Unicode strings.
|
|
141
|
+
|
|
142
|
+
Single line strings must be contained within a single line, and can be constructed using following literal syntax:
|
|
143
|
+
|
|
144
|
+
```firefly
|
|
145
|
+
"Hello, World!"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Multiline string literals begin and end with three double quotes, and may contain newlines:
|
|
149
|
+
|
|
150
|
+
```firefly
|
|
151
|
+
"""
|
|
152
|
+
Once upon a time,
|
|
153
|
+
in a land far, far away...
|
|
154
|
+
"""
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
There's an operator for string concatenation:
|
|
158
|
+
|
|
159
|
+
```firefly
|
|
160
|
+
"ban" + "ana" == "banana"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The comparison operators are supported, but note that they compare the string as a sequence of `Char` values rather than by any language-specific ordering.
|
|
164
|
+
|
|
165
|
+
The escape mechanism is the same as with the `Char` type.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# List
|
|
169
|
+
|
|
170
|
+
Values of the `List[T]` types are immutable sequences of `T` values, where `T` is a type of your choice.
|
|
171
|
+
|
|
172
|
+
They can be constructed using list literals:
|
|
173
|
+
|
|
174
|
+
```firefly
|
|
175
|
+
[1, 2, 3] // A List[Int] with three elements
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Note that commas can be omitted when they would occur before a newline:
|
|
179
|
+
|
|
180
|
+
```firefly
|
|
181
|
+
[
|
|
182
|
+
1
|
|
183
|
+
2
|
|
184
|
+
3
|
|
185
|
+
]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Lists can be flattened into other lists using the spread syntax:
|
|
189
|
+
|
|
190
|
+
```firefly
|
|
191
|
+
[1, ...[2, 3], 4, 5] == [1, 2, 3, 4, 5]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The comparison operators are available for values of type `List[T]` when they're available for values of type `T`.
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# Unit
|
|
198
|
+
|
|
199
|
+
The `Unit` type has just one possible value, and is used as the return type for functions that have no interesting return value.
|
|
200
|
+
|
|
201
|
+
The type is defined in `ff:core` as follows:
|
|
202
|
+
|
|
203
|
+
```firefly
|
|
204
|
+
data Unit {
|
|
205
|
+
Unit
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
If you omit the return type, the `Unit` type is assumed. If you don't end a function with an expression, the `Unit` value is returned.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Functions and methods
|
|
2
|
+
|
|
3
|
+
There are 5 kinds of functions in Firefly:
|
|
4
|
+
|
|
5
|
+
* Top-level functions
|
|
6
|
+
* Local functions
|
|
7
|
+
* Methods
|
|
8
|
+
* Anonymous functions
|
|
9
|
+
* Trait functions
|
|
10
|
+
|
|
11
|
+
Trait functions are covered in section [Traits and instances](traits-and-instances), the rest are covered below.
|
|
12
|
+
|
|
13
|
+
# Top-level functions
|
|
14
|
+
|
|
15
|
+
Functions at the top level are defined like this:
|
|
16
|
+
|
|
17
|
+
```firefly
|
|
18
|
+
add(a: Int, b: Int): Int {
|
|
19
|
+
let sum = a + b
|
|
20
|
+
sum
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This function takes two arguments of type `Int` and returns their sum as an `Int`. A function with a return type other than `Unit`, must have an expression as the last statement, which is returned. In the example above, the value of `sum` is returned.
|
|
25
|
+
|
|
26
|
+
When a function returns Unit, it can end with any statement.
|
|
27
|
+
|
|
28
|
+
```firefly
|
|
29
|
+
class Counter(mutable value: Int)
|
|
30
|
+
|
|
31
|
+
reset(counter: Counter): Unit {
|
|
32
|
+
counter.value = 0
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The example above defines a type `Counter` with a mutable field. The function `reset` sets this value to zero.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
A `Unit` return type can be omitted in a function definition, like this:
|
|
40
|
+
|
|
41
|
+
```firefly
|
|
42
|
+
reset(counter: Counter) {
|
|
43
|
+
counter.value = 0
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The parameter types must be declares expitly and the parentheses are required, even if there are no parameters. The function name and parameter names must start with a lowercase letter.
|
|
48
|
+
|
|
49
|
+
# Type parameters
|
|
50
|
+
|
|
51
|
+
In a function signature, you can introduce a list of type parameters enclosed in square brackets, immediately following the function name. These type parameters can be used in the rest of the signature.
|
|
52
|
+
|
|
53
|
+
The following example defines a function `swap` that takes any Pair and returns a Pair with first and second values swapped:
|
|
54
|
+
|
|
55
|
+
```firefly
|
|
56
|
+
swap[A, B](pair: Pair[A, B]): Pair[B, A] {
|
|
57
|
+
Pair(pair.second, pair.first)
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Two type parameters `A` and `B` are first introduced in the square brackets. These type parameters are swapped in the input and return type, expressing the value swap at type level. The type parameters are unbuilded in the sense that `swap` may be called with `A` and `B` replaced by any types.
|
|
62
|
+
|
|
63
|
+
Type parameters can be bounded or constrained like this:
|
|
64
|
+
|
|
65
|
+
```firefly
|
|
66
|
+
same[E: Equal](pair: Pair[E, E]): Bool {
|
|
67
|
+
pair.first == pair.second
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The type parameter `E` must implement the Equal trait, which is required to perform equality comparisons. The section on [traits and instances](traits-and-instances) will discuss constraints in more detail.
|
|
72
|
+
|
|
73
|
+
Firefly cannot operate on the concrete types of type parameters at runtime. The behavior of a function is limited to what is specified in the function signature.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Calling functions
|
|
77
|
+
|
|
78
|
+
Functions are called like this:
|
|
79
|
+
|
|
80
|
+
```firefly
|
|
81
|
+
add(1, 2) // 3
|
|
82
|
+
same(Pair('A', 'a')) // False
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Just like variants, the arguments can be named, and given out of order, like this:
|
|
86
|
+
|
|
87
|
+
```firefly
|
|
88
|
+
add(b = 2, a = 1)
|
|
89
|
+
add(b = 2, 1) // Same as above
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
# Default Values
|
|
93
|
+
|
|
94
|
+
Function parameters in Firefly can have default values, which are used when no argument is provided for that parameter during a function call. Default values are specified in the function signature by assigning a value to the parameter.
|
|
95
|
+
|
|
96
|
+
Here’s an example of a function with a default value:
|
|
97
|
+
|
|
98
|
+
```firefly
|
|
99
|
+
add(a: Int, b: Int = 1): Int {
|
|
100
|
+
a + b
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
When called without the second arguments, the function will use the default value:
|
|
105
|
+
|
|
106
|
+
```firefly
|
|
107
|
+
greet(1) // returns 2
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If an argument is provided, it overrides the default:
|
|
111
|
+
|
|
112
|
+
```firefly
|
|
113
|
+
greet(1, 2) // returns 3
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Recursion
|
|
118
|
+
|
|
119
|
+
Function definitions can be recursive, meaning that a function can call itself. Here’s an example of a recursive function that calculates the factorial of a number:
|
|
120
|
+
|
|
121
|
+
```firefly
|
|
122
|
+
factorial(n: Int): Int {
|
|
123
|
+
if(n == 0) {
|
|
124
|
+
1
|
|
125
|
+
} else {
|
|
126
|
+
n * factorial(n - 1)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When calling `factorial(0)`, the base case is triggered, returning `1`. Calling the function with a positive integer will recursively calculate the factorial. However, calling it with negative values will result in infinite recursion, as no base case exists for such input.
|
|
132
|
+
|
|
133
|
+
# Tail Recursion
|
|
134
|
+
|
|
135
|
+
In some recursive functions, the recursive call is the last operation performed before returning the result. This is *tail recursion*. The Firefly compiler can optimize tail recursive calls, avoiding the buildup of function calls on the stack.
|
|
136
|
+
|
|
137
|
+
You can use the `tailcall` keyword to explicitly mark a recursive call as tail-recursive, ensuring the compiler applies the optimization.
|
|
138
|
+
|
|
139
|
+
Here is a tail-recursive implementation of the factorial function:
|
|
140
|
+
|
|
141
|
+
```firefly
|
|
142
|
+
factorial(n: Int, acc: Int = 1): Int {
|
|
143
|
+
if(n == 0) {
|
|
144
|
+
acc
|
|
145
|
+
} else {
|
|
146
|
+
tailcall factorial(n - 1, n * acc)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
This version introduces an additional parameter, `acc`, which acts as an accumulator to hold the running result of the factorial calculation. It is initialized to 1 by default, ensuring that when the function is first called with a single argument, the computation starts correctly. By moving the multiplication into the recursive call via the accumulator, the call becomes a tail-call, allowing the Firefly compiler to optimize it.
|
|
152
|
+
|
|
153
|
+
# Anonymous functions
|
|
154
|
+
|
|
155
|
+
Firefly has anonymous functions, where the function and the parameters do not have names. They have a concrete type without type parameters. They are first-class values and can be assigned and passed around like other values. Here is an example:
|
|
156
|
+
|
|
157
|
+
```firefly
|
|
158
|
+
let next: Int => Int = {i => i + 1}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The fat arrow (`=>`) is used both in the type and in the definition. This arrow is always part of the function type and separates the parameter types from the return type. In the anonymous function definition, the arrow separates the arguments from the function body and can be omitted in some cases, as shown later.
|
|
162
|
+
|
|
163
|
+
This is an anonymous function taking no arguments:
|
|
164
|
+
|
|
165
|
+
```firefly
|
|
166
|
+
let life: () => Int = {42}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This is the anonymous function taking no arguments, returning `Unit`:
|
|
170
|
+
|
|
171
|
+
```firefly
|
|
172
|
+
let uhit: () => Unit = {}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This anonymous function takes multiple arguments:
|
|
176
|
+
|
|
177
|
+
```firefly
|
|
178
|
+
let plus: (Int, Int) => Int = {a, b => a + b}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Anonymous function are called like named function.
|
|
182
|
+
|
|
183
|
+
```firefly
|
|
184
|
+
life() // returns 42
|
|
185
|
+
unit() // returns unit
|
|
186
|
+
next(1) // returns 2
|
|
187
|
+
plus(1, 2) // returns 3
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Placeholders...
|
|
191
|
+
|
|
192
|
+
```firefly
|
|
193
|
+
let identity: Int => Int = {_}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# Local functions
|
|
198
|
+
|
|
199
|
+
Local functions are declared exactly like top-level functions but with the `function` keyword in front of the signature, like this:
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
```firefly
|
|
203
|
+
function square(n: Int): Int {
|
|
204
|
+
n * n
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
# Methods
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Modules and packages
|
|
2
|
+
|
|
3
|
+
In Firefly, source code is organized in packages, which can be versioned, released and depended on.
|
|
4
|
+
Inside packages, there are modules, which are individual files that can be imported from other modules.
|
|
5
|
+
|
|
6
|
+
The minimal package consists of just a single module file, and nothing else.
|
|
7
|
+
Such a module can contain the following top level constructs:
|
|
8
|
+
|
|
9
|
+
```firefly
|
|
10
|
+
// Package information
|
|
11
|
+
package mygroup:mypackage:1.2.3
|
|
12
|
+
dependency ff:webserver:0.0.0
|
|
13
|
+
include "node_modules"
|
|
14
|
+
|
|
15
|
+
// Module imports
|
|
16
|
+
import WebServer from ff:webserver
|
|
17
|
+
|
|
18
|
+
// Named constants
|
|
19
|
+
answer: Int = 42
|
|
20
|
+
|
|
21
|
+
// Function definitions
|
|
22
|
+
f(x: Int): Int {
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Type definitions
|
|
27
|
+
data Point(x: Int, y: Int)
|
|
28
|
+
|
|
29
|
+
// Method definitions
|
|
30
|
+
extend self: Point {
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Traits and trait instances
|
|
35
|
+
trait T: HasCenter {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
instance Point: HasCenter {
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
In a multi-file package, the package information is instead specified in a separate `package.ff` file which must live in the `.firefly/` subdirectory. A typical multi-file package looks like this:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
mypackage/
|
|
47
|
+
.firefly/
|
|
48
|
+
package.ff
|
|
49
|
+
MyModule.ff
|
|
50
|
+
MyOtherModule.ff
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The two module files here `MyModule.ff` and `MyOtherModule.ff` defines the modules `MyModule` and `MyOtherModule` respectively. Module files must start with a capital letter.
|
|
54
|
+
|
|
55
|
+
In general, identifiers of any kind in Firefly must start with an ASCII letter, and can only contain ASCII letters and numbers. This also applies to module and package names.
|
|
56
|
+
|
|
57
|
+
Apart from containing the `package.ff` file, the `.firefly/` subdirectory is used for various compiler output and can contain an `include/` directory with JavaScript that you want to include verbatim into the build via the `include` directive.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Package information
|
|
61
|
+
|
|
62
|
+
The `package` keyword specifies the group name, package name and major.minor.patch package version:
|
|
63
|
+
|
|
64
|
+
```firefly
|
|
65
|
+
package mygroup:mypackage:1.2.3
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A group is a person, organization or similar entity that's allowed to publish packages under that group name.
|
|
69
|
+
|
|
70
|
+
If present, the `package` keyword must be the first token in the file. In a single file package, all the package information goes before any other content.
|
|
71
|
+
|
|
72
|
+
The `dependency` keyword specifies the group name, package name and major.minor.patch package version of a package that is a dependency of this package:
|
|
73
|
+
|
|
74
|
+
```firefly
|
|
75
|
+
dependency ff:webserver:0.0.0
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
There may be zero or more dependencies. If there are conflicting versions of the same package in the dependencies or transitive dependencies, the first version that's encountered in a breadth first search from top to bottom will be used.
|
|
79
|
+
|
|
80
|
+
The `include` keyword includes files verbatim in the JavaScript that is emitted by the compiler:
|
|
81
|
+
|
|
82
|
+
```firefly
|
|
83
|
+
include "node_modules"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This instructs the compiler to copy the file or directory `mypackage/.firefly/include/node_modules` verbatim into the `mypackage/.firefly/ouput/node/mygroup/mypackage/node/node_modules` directory. It doesn't do anything for the browser target.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Imports
|
|
90
|
+
|
|
91
|
+
To access the symbols that a module exports, it is necessary to import it:
|
|
92
|
+
|
|
93
|
+
```firefly
|
|
94
|
+
import WebServer from ff:webserver
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This imports the `WebServer` module from the `ff:webserver` package, which must have been declared as a dependency.
|
|
98
|
+
|
|
99
|
+
Symbols exported from the module can then be accessed using the `WebServer.` prefix:
|
|
100
|
+
|
|
101
|
+
```firefly
|
|
102
|
+
WebServer.new(system, "localhost", 8080)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Types, variants and traits are available under the prefix, but can also be accessed with no prefix at all. In the case of naming collisions between these, the last import wins.
|
|
106
|
+
|
|
107
|
+
If two imported modules have the same name, or a different prefix is desired, the symbols can be imported under a different prefix:
|
|
108
|
+
|
|
109
|
+
```firefly
|
|
110
|
+
import WebServer as W from ff:webserver
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The symbols can then be accessed using the `W.` prefix:
|
|
114
|
+
|
|
115
|
+
```firefly
|
|
116
|
+
W.new(system, "localhost", 8080)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# Exports
|
|
121
|
+
|
|
122
|
+
Currently, all top level definitions are automatically exported. This is likely to change in the future.
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Main
|
|
126
|
+
|
|
127
|
+
In Firefly, there are three targets, each with its own main function:
|
|
128
|
+
|
|
129
|
+
```firefly
|
|
130
|
+
nodeMain(system: NodeSystem) {
|
|
131
|
+
system.writeLine("Hello server!")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
browserMain(system: BrowserSystem) {
|
|
135
|
+
system.writeLine("Hello browser!")
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
buildMain(system: BuildSystem) {
|
|
139
|
+
system.writeLine("Hello build!")
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The three main functions may coexist in the same file.
|
|
144
|
+
|
|
145
|
+
The `nodeMain` function runs when you run your executable.
|
|
146
|
+
|
|
147
|
+
The `browserMain` function runs in the browser.
|
|
148
|
+
|
|
149
|
+
The `buildMain` function runs when you build your program.
|
|
150
|
+
|
|
151
|
+
The `system` parameter is an object that lets you do I/O in the target system.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Constants
|
|
155
|
+
|
|
156
|
+
Named constants may be defined at the top level, and must have an explict type:
|
|
157
|
+
|
|
158
|
+
```firefly
|
|
159
|
+
answer: Int = 42
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Here `answer` is defined to be an `Int` with the value `42`.
|
|
163
|
+
|
|
164
|
+
There's no global state in Firefly, and to enforce this, the type of a named constant must have be declared with the `data` or `newtype` keyword.
|
|
165
|
+
|
|
166
|
+
Named constants that occur on the right hand side must not directly or indirecly refer to the named constant being defined.
|
|
167
|
+
|
|
168
|
+
Neither of the requirements are enforced currently, but this is likely to change in the future.
|