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,365 +1,365 @@
1
- # Functions and methods
2
-
3
- There are 5 kinds of functions in Firefly:
4
-
5
- * Top-level functions
6
- * Anonymous functions
7
- * Local functions
8
- * Methods
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 unbounded 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
- add(1) // returns 2
108
- ```
109
-
110
- If an argument is provided, it overrides the default:
111
-
112
- ```firefly
113
- add(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
- In firefly anonymous functions are written in curlybrases and constucted like this:
156
-
157
- ```firefly
158
- {a, b =>
159
- let sum = a + b
160
- sum
161
- }
162
- ```
163
-
164
- This anonymous function takes two arguments and returns their sum. Like named functions, the body is a sequence of statements where the last expression is returned.
165
-
166
- Anonymous functions are often used right away, like below:
167
-
168
- ```firefly
169
- [1, 2, 3].map({x => x + 1}) // Returns [2, 3, 4]
170
- ```
171
-
172
- An anonymous function that increments the given value by one is passed as argument to the method `map` working on lists.
173
-
174
- These functions are anonymous in the sense that they do not bring a name into scope themselves. They are just expressions that construct a function value. Like all other values, they can be assigned to variables, passed as arguments, or returned from other functions. But unlike other values, they can also be called.
175
-
176
- This is in contrast to named functions, which are not first-class in Firefly. The name of a top-level function can only be called but is not an expression in Firefly. To pass a top-level function as an argument, for instance, it must be converted to an anonymous function first.
177
-
178
- The type of function values are writen like this:
179
-
180
- ```firefly
181
- Int => Int // One parameter
182
- (Int, Int) => Int // Multiple parameters
183
- () => Int // No parameters
184
- ```
185
-
186
- The type of an anonymous function cannot be written explicitly in the definition but is inferred from its usage. It will always have a monomorphic type where the argument and return types are concrete types.
187
-
188
- Here are some examples of anonymous functions assigned to variables explicitly given a type.
189
-
190
- Anonymous function without parameters are written without the arrow (`=>`), like this:
191
-
192
- ```firefly
193
- let life: () => Int = {42}
194
- ```
195
-
196
- This is an anonymous function taking no arguments and returning `Unit`:
197
-
198
- ```firefly
199
- let unit: () => Unit = {}
200
- ```
201
-
202
- This is an anonymous function that increments its input by one:
203
-
204
- ```firefly
205
- let next: Int => Int = {i => i + 1}
206
- ```
207
-
208
- This anonymous function takes multiple arguments:
209
-
210
- ```firefly
211
- let plus: (Int, Int) => Int = {a, b => a + b}
212
- ```
213
-
214
- Anonymous function are called like named function.
215
-
216
- ```firefly
217
- life() // returns 42
218
- unit() // returns unit
219
- next(1) // returns 2
220
- plus(1, 2) // returns 3
221
- ```
222
-
223
- Parameter names are not part of the function type, and likewise, anonymous functions cannot be called with named arguments. The same goes for default argument values, which are not supported for anonymous functions.
224
-
225
- The parameter list and the function arrow can be omitted when the parameters are only used once in the function body. In such cases, the parameters in the body are replaced with underscores (`_`), like this:
226
-
227
-
228
- ```firefly
229
- let next: Int => Int = {_ + 1}
230
- let plus: (Int, Int) => Int = {_ + _}
231
- let identity: Int => Int = {_}
232
- ```
233
-
234
- These underscores, or anonymous parameters, always belong to the nearest anonymous function. Consider the following function:
235
-
236
- ```firefly
237
- let f: Int => Int = {{_ + 1}(_)}
238
- ```
239
-
240
- In this code, there is an outer and an inner anonymous function, both taking one argument. The first underscore belongs to the inner function, which is called immediately by the outer function with the outer function's anonymous parameter as the argument.
241
-
242
-
243
- # Trailing Anonymous Function Arguments
244
-
245
- Firefly has a special syntax for calling functions with function arguments. When a call has a sequence of literal anonymous functions as the last arguments, these arguments may be given using this special syntax. Consider the `if` function from the standard library, with this signature:
246
-
247
- ```firefly
248
- if[T](condition: Bool, body: () => T): Option[T]
249
- ```
250
-
251
- The `if` function takes two parameters, where the last is a function. Calling `if` with the standard syntax could look like this:
252
-
253
- ```firefly
254
- if(x == 0, {"Zero"})
255
- ```
256
-
257
- Using the special syntax for trailing anonymous function arguments, it looks like this:
258
-
259
- ```firefly
260
- if(x == 0) {"Zero"}
261
- ```
262
-
263
- With this syntax, the anonymous function is written after the call parentheses. Multiple trailing function arguments may be given in sequence. Consider the `while` function from the standard library, with this signature:
264
-
265
- ```firefly
266
- while(condition: () => Bool, body: () => Unit): Unit
267
- ```
268
-
269
- The `while` function takes two parameters, both functions. Using the special syntax, a call to `while` may look like this:
270
-
271
- ```firefly
272
- while {array.size() < 5} {
273
- array.push("X")
274
- }
275
- ```
276
-
277
- The code above will push a string to an array while the size of the array is less than 5.
278
-
279
- This syntax for trailing anonymous function arguments allows the use of `if` and `while` to resemble constructs in languages such as C and JavaScript, where these constructs are built-in keywords rather than functions.
280
-
281
- # Trailing Colon Function Argument
282
-
283
- Firefly has a variation of the trailing anonymous function argument syntax. The very last anonymous function argument may be written after a colon, without curly braces. The purpose of this syntax is to avoid aditional indentation.
284
-
285
- The example below calls `if` with a trailing colon function argument:
286
-
287
- ```firefly
288
- safeFactorial(n: Int, acc: Int = 1): Option[Int] {
289
- if(n >= 0):
290
- factorial(n)
291
- }
292
- ```
293
-
294
- In this example, the `if` function is called with the condition `n >= 0` and an anonymous function that computes `factorial(n)`. If `n` is negative, the `if` function returns `None` and so does `safeFactorial`. Using the colon syntax, the `safeFactorial` functions continues unindented otherwise.
295
-
296
-
297
- # Local functions
298
-
299
- Local functions are declared exactly like top-level functions but with the `function` keyword in front of the signature, like this:
300
-
301
- ```firefly
302
- function square(n: Int): Int {
303
- n * n
304
- }
305
- ```
306
-
307
- The above local function definition is a statement, similar to local variables declared with `let`. The function name `square` will be in scope for the rest of the code block.
308
-
309
- Furthermore, local functions declared in sequence are in scope within each other's bodies, allowing them to be mutually recursive.
310
-
311
- # Methods
312
-
313
- Firefly has methods, which are called like this:
314
-
315
- ```firefly
316
- Some(1).isEmpty() // False
317
- Some(1).map({_ + 1}) // Some(2)
318
- ```
319
-
320
- The examples above, calls the two methods `isEmpty` and `map` defined on `Option`. The code below, shows how these methods are defined in `ff:core` package:
321
-
322
- ```firefly
323
- extend self[T]: Option[T] {
324
- isEmpty(): Bool {...}
325
- map[R](body: T => R): Option[R] {...}
326
- }
327
- ```
328
-
329
- The `extend` keyword is used to declare methods on values of a given type. The code above extend values of type `Option[T]` with two methods. The identifier `self` holds a reference to the value receiving the methods. The square bracket right after the self variable are optional and used to introduce type variables used to express the type extended with methods.
330
-
331
- The two methods above are defined for all values of type `Option[T]`, but methods can be også be defined for a more narrow targer type, like `flatten` below:
332
-
333
- ```firefly
334
- extend self[T]: Option[Option[T]] {
335
- flatten(): Option[T] {...}
336
- }
337
- ```
338
-
339
- The extend block above declares `self` as ` Option[Option[T]]`, and likewise will only define `flatten` for options types of options.
340
-
341
- The scope of a method can also be narrowed down with trait constraints.
342
-
343
- ```firefly
344
- extend self[T: Equal]: Option[T] {
345
- contains(value: T): Bool {...}
346
- }
347
- ```
348
-
349
- In code above, the extend block defines methods for the target type `Option[T]`, but only when `T` implements the `Equal` trait.
350
-
351
- Extend blocks must reside in the same module at definition of the type that are extended with methods.
352
-
353
- Methods are equivalent to top level functions in terms of expressibility but differnt in terms of scoping. Each type definition has its own method scope.
354
-
355
- # Special method call syntax
356
-
357
- ```firefly
358
- if(x == 1) {"One"} else {"Several"}
359
- Some(1).map {_ + 1} // Some(2)
360
-
361
- ```
362
-
363
- # Trait functions
364
-
1
+ # Functions and methods
2
+
3
+ There are 5 kinds of functions in Firefly:
4
+
5
+ * Top-level functions
6
+ * Anonymous functions
7
+ * Local functions
8
+ * Methods
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 unbounded 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
+ add(1) // returns 2
108
+ ```
109
+
110
+ If an argument is provided, it overrides the default:
111
+
112
+ ```firefly
113
+ add(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
+ In firefly anonymous functions are written in curlybrases and constucted like this:
156
+
157
+ ```firefly
158
+ {a, b =>
159
+ let sum = a + b
160
+ sum
161
+ }
162
+ ```
163
+
164
+ This anonymous function takes two arguments and returns their sum. Like named functions, the body is a sequence of statements where the last expression is returned.
165
+
166
+ Anonymous functions are often used right away, like below:
167
+
168
+ ```firefly
169
+ [1, 2, 3].map({x => x + 1}) // Returns [2, 3, 4]
170
+ ```
171
+
172
+ An anonymous function that increments the given value by one is passed as argument to the method `map` working on lists.
173
+
174
+ These functions are anonymous in the sense that they do not bring a name into scope themselves. They are just expressions that construct a function value. Like all other values, they can be assigned to variables, passed as arguments, or returned from other functions. But unlike other values, they can also be called.
175
+
176
+ This is in contrast to named functions, which are not first-class in Firefly. The name of a top-level function can only be called but is not an expression in Firefly. To pass a top-level function as an argument, for instance, it must be converted to an anonymous function first.
177
+
178
+ The type of function values are writen like this:
179
+
180
+ ```firefly
181
+ Int => Int // One parameter
182
+ (Int, Int) => Int // Multiple parameters
183
+ () => Int // No parameters
184
+ ```
185
+
186
+ The type of an anonymous function cannot be written explicitly in the definition but is inferred from its usage. It will always have a monomorphic type where the argument and return types are concrete types.
187
+
188
+ Here are some examples of anonymous functions assigned to variables explicitly given a type.
189
+
190
+ Anonymous function without parameters are written without the arrow (`=>`), like this:
191
+
192
+ ```firefly
193
+ let life: () => Int = {42}
194
+ ```
195
+
196
+ This is an anonymous function taking no arguments and returning `Unit`:
197
+
198
+ ```firefly
199
+ let unit: () => Unit = {}
200
+ ```
201
+
202
+ This is an anonymous function that increments its input by one:
203
+
204
+ ```firefly
205
+ let next: Int => Int = {i => i + 1}
206
+ ```
207
+
208
+ This anonymous function takes multiple arguments:
209
+
210
+ ```firefly
211
+ let plus: (Int, Int) => Int = {a, b => a + b}
212
+ ```
213
+
214
+ Anonymous function are called like named function.
215
+
216
+ ```firefly
217
+ life() // returns 42
218
+ unit() // returns unit
219
+ next(1) // returns 2
220
+ plus(1, 2) // returns 3
221
+ ```
222
+
223
+ Parameter names are not part of the function type, and likewise, anonymous functions cannot be called with named arguments. The same goes for default argument values, which are not supported for anonymous functions.
224
+
225
+ The parameter list and the function arrow can be omitted when the parameters are only used once in the function body. In such cases, the parameters in the body are replaced with underscores (`_`), like this:
226
+
227
+
228
+ ```firefly
229
+ let next: Int => Int = {_ + 1}
230
+ let plus: (Int, Int) => Int = {_ + _}
231
+ let identity: Int => Int = {_}
232
+ ```
233
+
234
+ These underscores, or anonymous parameters, always belong to the nearest anonymous function. Consider the following function:
235
+
236
+ ```firefly
237
+ let f: Int => Int = {{_ + 1}(_)}
238
+ ```
239
+
240
+ In this code, there is an outer and an inner anonymous function, both taking one argument. The first underscore belongs to the inner function, which is called immediately by the outer function with the outer function's anonymous parameter as the argument.
241
+
242
+
243
+ # Trailing Anonymous Function Arguments
244
+
245
+ Firefly has a special syntax for calling functions with function arguments. When a call has a sequence of literal anonymous functions as the last arguments, these arguments may be given using this special syntax. Consider the `if` function from the standard library, with this signature:
246
+
247
+ ```firefly
248
+ if[T](condition: Bool, body: () => T): Option[T]
249
+ ```
250
+
251
+ The `if` function takes two parameters, where the last is a function. Calling `if` with the standard syntax could look like this:
252
+
253
+ ```firefly
254
+ if(x == 0, {"Zero"})
255
+ ```
256
+
257
+ Using the special syntax for trailing anonymous function arguments, it looks like this:
258
+
259
+ ```firefly
260
+ if(x == 0) {"Zero"}
261
+ ```
262
+
263
+ With this syntax, the anonymous function is written after the call parentheses. Multiple trailing function arguments may be given in sequence. Consider the `while` function from the standard library, with this signature:
264
+
265
+ ```firefly
266
+ while(condition: () => Bool, body: () => Unit): Unit
267
+ ```
268
+
269
+ The `while` function takes two parameters, both functions. Using the special syntax, a call to `while` may look like this:
270
+
271
+ ```firefly
272
+ while {array.size() < 5} {
273
+ array.push("X")
274
+ }
275
+ ```
276
+
277
+ The code above will push a string to an array while the size of the array is less than 5.
278
+
279
+ This syntax for trailing anonymous function arguments allows the use of `if` and `while` to resemble constructs in languages such as C and JavaScript, where these constructs are built-in keywords rather than functions.
280
+
281
+ # Trailing Colon Function Argument
282
+
283
+ Firefly has a variation of the trailing anonymous function argument syntax. The very last anonymous function argument may be written after a colon, without curly braces. The purpose of this syntax is to avoid aditional indentation.
284
+
285
+ The example below calls `if` with a trailing colon function argument:
286
+
287
+ ```firefly
288
+ safeFactorial(n: Int, acc: Int = 1): Option[Int] {
289
+ if(n >= 0):
290
+ factorial(n)
291
+ }
292
+ ```
293
+
294
+ In this example, the `if` function is called with the condition `n >= 0` and an anonymous function that computes `factorial(n)`. If `n` is negative, the `if` function returns `None` and so does `safeFactorial`. Using the colon syntax, the `safeFactorial` functions continues unindented otherwise.
295
+
296
+
297
+ # Local functions
298
+
299
+ Local functions are declared exactly like top-level functions but with the `function` keyword in front of the signature, like this:
300
+
301
+ ```firefly
302
+ function square(n: Int): Int {
303
+ n * n
304
+ }
305
+ ```
306
+
307
+ The above local function definition is a statement, similar to local variables declared with `let`. The function name `square` will be in scope for the rest of the code block.
308
+
309
+ Furthermore, local functions declared in sequence are in scope within each other's bodies, allowing them to be mutually recursive.
310
+
311
+ # Methods
312
+
313
+ Firefly has methods, which are called like this:
314
+
315
+ ```firefly
316
+ Some(1).isEmpty() // False
317
+ Some(1).map({_ + 1}) // Some(2)
318
+ ```
319
+
320
+ The examples above, calls the two methods `isEmpty` and `map` defined on `Option`. The code below, shows how these methods are defined in `ff:core` package:
321
+
322
+ ```firefly
323
+ extend self[T]: Option[T] {
324
+ isEmpty(): Bool {...}
325
+ map[R](body: T => R): Option[R] {...}
326
+ }
327
+ ```
328
+
329
+ The `extend` keyword is used to declare methods on values of a given type. The code above extend values of type `Option[T]` with two methods. The identifier `self` holds a reference to the value receiving the methods. The square bracket right after the self variable are optional and used to introduce type variables used to express the type extended with methods.
330
+
331
+ The two methods above are defined for all values of type `Option[T]`, but methods can be også be defined for a more narrow targer type, like `flatten` below:
332
+
333
+ ```firefly
334
+ extend self[T]: Option[Option[T]] {
335
+ flatten(): Option[T] {...}
336
+ }
337
+ ```
338
+
339
+ The extend block above declares `self` as ` Option[Option[T]]`, and likewise will only define `flatten` for options types of options.
340
+
341
+ The scope of a method can also be narrowed down with trait constraints.
342
+
343
+ ```firefly
344
+ extend self[T: Equal]: Option[T] {
345
+ contains(value: T): Bool {...}
346
+ }
347
+ ```
348
+
349
+ In code above, the extend block defines methods for the target type `Option[T]`, but only when `T` implements the `Equal` trait.
350
+
351
+ Extend blocks must reside in the same module at definition of the type that are extended with methods.
352
+
353
+ Methods are equivalent to top level functions in terms of expressibility but differnt in terms of scoping. Each type definition has its own method scope.
354
+
355
+ # Special method call syntax
356
+
357
+ ```firefly
358
+ if(x == 1) {"One"} else {"Several"}
359
+ Some(1).map {_ + 1} // Some(2)
360
+
361
+ ```
362
+
363
+ # Trait functions
364
+
365
365
  Trait functions are covered in the section about [traits and instances](traits-and-instances)