firefly-compiler 0.4.77 → 0.4.79

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 (135) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +153 -153
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +257 -257
  6. package/compiler/Compiler.ff +227 -227
  7. package/compiler/Dependencies.ff +187 -187
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/JsEmitter.ff +946 -946
  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-lock.json +564 -394
  14. package/core/.firefly/include/package.json +5 -5
  15. package/core/.firefly/include/prepare.sh +1 -1
  16. package/core/.firefly/package.ff +2 -2
  17. package/core/Array.ff +265 -265
  18. package/core/Atomic.ff +64 -64
  19. package/core/Box.ff +7 -7
  20. package/core/BrowserSystem.ff +40 -40
  21. package/core/BuildSystem.ff +148 -148
  22. package/core/Crypto.ff +96 -96
  23. package/core/Equal.ff +36 -36
  24. package/core/HttpClient.ff +148 -148
  25. package/core/JsSystem.ff +69 -69
  26. package/core/Json.ff +434 -434
  27. package/core/List.ff +486 -486
  28. package/core/Lock.ff +144 -144
  29. package/core/NodeSystem.ff +216 -216
  30. package/core/Ordering.ff +161 -161
  31. package/core/Path.ff +401 -401
  32. package/core/Random.ff +134 -134
  33. package/core/RbMap.ff +216 -216
  34. package/core/Show.ff +43 -43
  35. package/core/SourceLocation.ff +68 -68
  36. package/core/String.ff +16 -0
  37. package/core/Task.ff +141 -141
  38. package/experimental/benchmarks/ListGrab.ff +23 -23
  39. package/experimental/benchmarks/ListGrab.java +55 -55
  40. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  41. package/experimental/benchmarks/Pyrotek45.java +64 -64
  42. package/experimental/bidirectional/Bidi.ff +88 -88
  43. package/experimental/random/Index.ff +53 -53
  44. package/experimental/random/Process.ff +120 -120
  45. package/experimental/random/Scrape.ff +51 -51
  46. package/experimental/random/Symbols.ff +73 -73
  47. package/experimental/random/Tensor.ff +52 -52
  48. package/experimental/random/Units.ff +36 -36
  49. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -38
  50. package/experimental/s3/S3TestPut.ff +16 -15
  51. package/experimental/tests/TestJson.ff +26 -26
  52. package/firefly.sh +0 -0
  53. package/fireflysite/.firefly/package.ff +4 -0
  54. package/fireflysite/CommunityOverview.ff +20 -0
  55. package/fireflysite/CountingButtonDemo.ff +58 -0
  56. package/fireflysite/DocumentParser.ff +218 -0
  57. package/fireflysite/ExamplesOverview.ff +40 -0
  58. package/fireflysite/FrontPage.ff +360 -0
  59. package/fireflysite/Guide.ff +411 -0
  60. package/fireflysite/GuideAll.ff +21 -0
  61. package/fireflysite/GuideBaseTypes.ff +168 -0
  62. package/fireflysite/GuideControlFlow.ff +212 -0
  63. package/fireflysite/GuideIntroduction.ff +52 -0
  64. package/fireflysite/Main.ff +137 -15
  65. package/fireflysite/MatchingPasswordsDemo.ff +82 -0
  66. package/fireflysite/PackagesOverview.ff +49 -0
  67. package/fireflysite/PostgresqlDemo.ff +34 -0
  68. package/fireflysite/Styles.ff +495 -0
  69. package/fireflysite/assets/NotoSansMono-Regular.ttf +0 -0
  70. package/fireflysite/assets/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf +0 -0
  71. package/fireflysite/assets/autocomplete-small.png +0 -0
  72. package/fireflysite/assets/autocomplete.png +0 -0
  73. package/fireflysite/assets/edit-time-error.png +0 -0
  74. package/fireflysite/assets/firefly-logo-notext.png +0 -0
  75. package/fireflysite/assets/firefly-logo-yellow.png +0 -0
  76. package/fireflysite/assets/markdown/ControlFlow.md +136 -0
  77. package/fireflysite/assets/markdown/Example.md +78 -0
  78. package/lsp/.firefly/package.ff +1 -1
  79. package/lsp/CompletionHandler.ff +828 -828
  80. package/lsp/Handler.ff +714 -714
  81. package/lsp/HoverHandler.ff +79 -79
  82. package/lsp/LanguageServer.ff +272 -272
  83. package/lsp/SignatureHelpHandler.ff +55 -55
  84. package/lsp/SymbolHandler.ff +181 -181
  85. package/lsp/TestReferences.ff +17 -17
  86. package/lsp/TestReferencesCase.ff +7 -7
  87. package/lsp/stderr.txt +1 -1
  88. package/lsp/stdout.txt +34 -34
  89. package/lux/.firefly/package.ff +1 -1
  90. package/lux/Css.ff +648 -648
  91. package/lux/CssTest.ff +48 -48
  92. package/lux/Lux.ff +487 -487
  93. package/lux/LuxEvent.ff +116 -116
  94. package/lux/Main.ff +123 -123
  95. package/lux/Main2.ff +143 -144
  96. package/output/js/ff/compiler/Builder.mjs +43 -43
  97. package/output/js/ff/compiler/Dependencies.mjs +3 -3
  98. package/output/js/ff/core/Array.mjs +59 -59
  99. package/output/js/ff/core/Atomic.mjs +36 -36
  100. package/output/js/ff/core/BrowserSystem.mjs +11 -11
  101. package/output/js/ff/core/BuildSystem.mjs +30 -30
  102. package/output/js/ff/core/Crypto.mjs +40 -40
  103. package/output/js/ff/core/HttpClient.mjs +56 -56
  104. package/output/js/ff/core/Json.mjs +147 -147
  105. package/output/js/ff/core/List.mjs +50 -50
  106. package/output/js/ff/core/Lock.mjs +97 -97
  107. package/output/js/ff/core/NodeSystem.mjs +83 -83
  108. package/output/js/ff/core/Ordering.mjs +8 -8
  109. package/output/js/ff/core/Path.mjs +231 -231
  110. package/output/js/ff/core/Random.mjs +56 -56
  111. package/output/js/ff/core/String.mjs +20 -0
  112. package/output/js/ff/core/Task.mjs +31 -31
  113. package/package.json +1 -1
  114. package/rpc/.firefly/package.ff +1 -1
  115. package/rpc/Rpc.ff +70 -70
  116. package/s3/.firefly/package.ff +1 -1
  117. package/s3/S3.ff +94 -94
  118. package/unsafejs/UnsafeJs.ff +19 -19
  119. package/vscode/LICENSE.txt +21 -21
  120. package/vscode/Prepublish.ff +15 -15
  121. package/vscode/README.md +16 -16
  122. package/vscode/client/package.json +22 -22
  123. package/vscode/client/src/extension.ts +104 -104
  124. package/vscode/icons/firefly-icon.svg +10 -10
  125. package/vscode/language-configuration.json +61 -61
  126. package/vscode/package-lock.json +3623 -3623
  127. package/vscode/package.json +15 -1
  128. package/vscode/snippets.json +241 -241
  129. package/vscode/syntaxes/firefly-markdown-injection.json +45 -0
  130. package/webserver/.firefly/include/package-lock.json +22 -16
  131. package/webserver/.firefly/include/package.json +5 -5
  132. package/webserver/.firefly/package.ff +2 -2
  133. package/webserver/WebServer.ff +685 -685
  134. package/websocket/.firefly/package.ff +1 -1
  135. package/websocket/WebSocket.ff +131 -131
@@ -0,0 +1,218 @@
1
+ import WebServer from ff:webserver // This is required to run the file.
2
+ import Guide
3
+
4
+ nodeMain(system: NodeSystem) {
5
+ let file = system.path("assets/markdown/ControlFlow.md")
6
+ let document = parse(file)
7
+ printSections(system, document)
8
+ }
9
+
10
+ parse(file: Path): List[Section] {
11
+ let content = file.readText()
12
+ let lines = content.split('\n')
13
+ let parser = DocumentParser(file.absolute(), lines)
14
+ parser.parseDocument()
15
+ }
16
+
17
+ class DocumentParser(
18
+ fileName: String
19
+ mutable lines: List[String]
20
+ )
21
+
22
+ extend self: DocumentParser {
23
+
24
+ parseDocument(): List[Section] {
25
+ iterate {self.parseSection()}
26
+ }
27
+
28
+ parseSection(): Option[Section] {
29
+ self.dropWhile {_.trim() == ""}
30
+ self.lines.first().{
31
+ | Some(line) {line.trim().removeFirst("#") | Some(heading)} =>
32
+ self.dropFirst()
33
+ let blocks = iterate {self.parseBlock()}
34
+ Some(Section(heading.trim(), blocks))
35
+ | _ => None
36
+ }
37
+ }
38
+
39
+ parseBlock(): Option[Block] {
40
+ self.dropWhile {_.trim() == ""}
41
+ let first = self.lines.first().map {_.trimStart()}
42
+ first.{
43
+ | Some(line) {line.removeFirst("```") | Some(l)} =>
44
+ self.dropFirst()
45
+ let isFirefly = l.startsWith("firefly")
46
+ let block = self.parseRemainingCodeBlock(isFirefly)
47
+ Some(block)
48
+ | Some(line) {line.first().any {_ != '#'}} =>
49
+ Some(self.parseParagraph())
50
+ | _ => None
51
+ }
52
+ }
53
+
54
+ parseParagraph(): Block {
55
+ let lines = self.consumeWhile {_.trim() != ""}
56
+ let parser = InlineParser(self.fileName, lines.join("\n"))
57
+ let inlines = parser.parseInlines()
58
+ Paragraph(inlines)
59
+ }
60
+
61
+ parseRemainingCodeBlock(isFirefly: Bool): Block {
62
+ let code = self.consumeWhile {!_.trimStart().startsWith("```")}
63
+ self.dropFirst(1)
64
+ CodeBlock(code.join("\n"), isFirefly)
65
+ }
66
+
67
+
68
+ dropFirst(count: Int = 1): Unit {
69
+ self.lines = self.lines.dropFirst(count)
70
+ }
71
+
72
+ dropWhile(body: String => Bool): Unit {
73
+ self.lines = self.lines.dropWhile(body)
74
+ }
75
+
76
+ consumeWhile(body: String => Bool): List[String] {
77
+ let taken = self.lines.takeWhile(body)
78
+ self.dropFirst(taken.size())
79
+ taken
80
+ }
81
+ }
82
+
83
+ class InlineParser(
84
+ fileName: String
85
+ mutable line: String
86
+ )
87
+
88
+ extend self: InlineParser {
89
+
90
+ parseInlines(): List[Inline] {
91
+ iterate {self.parseInline()}
92
+ }
93
+
94
+ parseInline(): Option[Inline] {
95
+ self.parseBold().orElse:
96
+ self.parseCode().orElse:
97
+ self.parseItalic().orElse:
98
+ self.parseText()
99
+ }
100
+
101
+ parseBold(): Option[Inline] {
102
+ self.line.removeFirst("**").flatMap {rest =>
103
+ indexOf(rest, "**").map {end =>
104
+ let text = rest.slice(0, end)
105
+ self.dropFirst(end + 4)
106
+ Bold(text)
107
+ }
108
+ }
109
+ }
110
+
111
+ parseItalic(): Option[Inline] {
112
+ self.line.removeFirst("_").flatMap {rest =>
113
+ indexOf(rest, "_").map {end =>
114
+ let text = rest.slice(0, end)
115
+ self.dropFirst(end + 2)
116
+ Italic(text)
117
+ }
118
+ }
119
+ }
120
+
121
+ parseCode(): Option[Inline] {
122
+ self.line.removeFirst("`").flatMap {rest =>
123
+ indexOf(rest, "`").map {end =>
124
+ let text = rest.slice(0, end)
125
+ self.dropFirst(end + 2)
126
+ Code(text)
127
+ }
128
+ }
129
+ }
130
+
131
+ parseText(): Option[Inline] {
132
+ mutable i = 0
133
+
134
+ while {
135
+ i < self.line.size() &&
136
+ self.line.grab(i) != '_' &&
137
+ self.line.grab(i) != '`' &&
138
+ (i == self.line.size() - 1 || self.line.grab(i) != '*' || self.line.grab(i + 1) != '*')
139
+ } {
140
+ i += 1
141
+ }
142
+
143
+ i.{
144
+ | 0 => None
145
+ | _ =>
146
+ let text = self.line.slice(0, i)
147
+ self.dropFirst(text.size())
148
+ Some(Text(text))
149
+ }
150
+ }
151
+
152
+ dropFirst(count: Int = 1): Unit {
153
+ self.line = self.line.dropFirst(count)
154
+ }
155
+ }
156
+
157
+ iterate[T](body: () => Option[T]): List[T] {
158
+ let array = Array.new()
159
+ doWhile {
160
+ body().{
161
+ | None => False
162
+ | Some(s) =>
163
+ array.push(s)
164
+ True
165
+ }
166
+ }
167
+ array.drain()
168
+ }
169
+
170
+ indexOf(string: String, searchString: String, position: Int = 0): Option[Int]
171
+ target js sync """
172
+ const i = string_.indexOf(searchString_, position_);
173
+ return i == -1 ? ff_core_Option.None() : ff_core_Option.Some(i);
174
+ """
175
+
176
+ printSections(system: NodeSystem, sections: List[Section]) {
177
+ sections.each {
178
+ | Section(heading, blocks) =>
179
+ system.writeLine("# " + heading)
180
+ blocks.each {
181
+ | Bullets(items) =>
182
+ system.writeLine(" Bullets:")
183
+ system.writeLine(" " + Show.show(items))
184
+ | CodeBlock(code, firefly) =>
185
+ system.writeLine(" Code:")
186
+ system.writeLine(" " + Show.show(firefly))
187
+ | Image(url) =>
188
+ system.writeLine(" Image:")
189
+ system.writeLine(" " + Show.show(url))
190
+ | LuxDemo(demo) =>
191
+ system.writeLine(" Lux:")
192
+ system.writeLine(" " + Show.show(demo))
193
+ | Paragraph(inlines) =>
194
+ system.writeLine(" Paragraph:")
195
+ inlines.each {
196
+ | Anchor(heading, title) =>
197
+ system.writeLine(" Anchor: " + Show.show(heading) + ", " + Show.show(title))
198
+ | Bold(text) =>
199
+ system.writeLine(" Bold: " + Show.show(text))
200
+ | Code(code, True) =>
201
+ system.writeLine(" Code (firefly): " + Show.show(code))
202
+ | Code(code, False) =>
203
+ system.writeLine(" Code: " + Show.show(code))
204
+ | Italic(text) =>
205
+ system.writeLine(" Italic: " + Show.show(text))
206
+ | Link(text, url) =>
207
+ system.writeLine(" Link: " + Show.show(text))
208
+ | Text(text) =>
209
+ system.writeLine(" Text: " + Show.show(text))
210
+ }
211
+ | Video(url) =>
212
+ system.writeLine(" Video:")
213
+ system.writeLine(" " + Show.show(url))
214
+ }
215
+ | SplitSection(heading, first, second) =>
216
+ system.writeLine("SPLIT")
217
+ }
218
+ }
@@ -0,0 +1,40 @@
1
+ import Guide
2
+ import CountingButtonDemo
3
+ import MatchingPasswordsDemo
4
+ import PostgresqlDemo
5
+
6
+ mock(): List[Document] {
7
+ [
8
+ new()
9
+ CountingButtonDemo.newDocument()
10
+ MatchingPasswordsDemo.newDocument()
11
+ PostgresqlDemo.newDocument()
12
+ ]
13
+ }
14
+
15
+ demos(): List[Demo] {
16
+ [
17
+ CountingButtonDemo.new()
18
+ MatchingPasswordsDemo.new()
19
+ ]
20
+ }
21
+
22
+ new(): Document {
23
+ ReadyDocument([
24
+ Section("Examples", [
25
+ Paragraph([
26
+ Text("Here you'll find examples that demonstrate what you can do with Firefly.")
27
+ Text("In fact, you're looking at an example right now - this site is")
28
+ Link("written in Firefly", "https://github.com/Ahnfelt/firefly-boot/tree/master/fireflysite")
29
+ Text("and so is the")
30
+ Link("Firefly compiler", "https://github.com/Ahnfelt/firefly-boot/tree/master/compiler")
31
+ Text("and")
32
+ Link("language server", "https://github.com/Ahnfelt/firefly-boot/tree/master/lsp")
33
+ Text(".")
34
+ ])
35
+ Paragraph([
36
+ Text("Check the list here for examples and demos.")
37
+ ])
38
+ ])
39
+ ])
40
+ }
@@ -0,0 +1,360 @@
1
+ import Guide
2
+ import CountingButtonDemo
3
+ import MatchingPasswordsDemo
4
+
5
+ new(): Document {
6
+ ReadyDocument([
7
+ /*Section("Is DX an add-on feature?", [
8
+ Paragraph([
9
+ Bold("We don't think so.")
10
+ Text("Firefly is designed from the ground up as a full stack language with great developer experience.")
11
+ ])
12
+ Image("/assets/autocomplete.png")
13
+ Paragraph([
14
+ Italic("Firefly in VSCode, showcasing instant type driven autocompletion and edit time error detection.")
15
+ Italic("An example of DX that's only possible for a certain class of languages. Read on for more Firefly DX.")
16
+ ])
17
+ ])*/
18
+ Section("The full stack programming language", [
19
+ Paragraph([
20
+ Text("Firefly doesn't just run in the browser and on the server,")
21
+ Text("it comes with a complete stack that allows you to implement full webapps.")
22
+ Text("Everything in the stack is designed to fit together nicely, all with excellent IDE support.")
23
+ Text("It's really easy to ")
24
+ Link("get started", "/guide/")
25
+ Text("and there's no magic.")
26
+ ])
27
+ ])
28
+ SplitSection(
29
+ "Single file webapps"
30
+ Paragraph([
31
+ Text("Firefly code runs in the browser and on the server, or even at build time.")
32
+ Text("When starting out, you can put everything in a single")
33
+ Code(".ff")
34
+ Text("file, including your dependency list.")
35
+ Text("As the code base grows, you can split it into multiple files or packages.")
36
+ ])
37
+ CodeBlock("""
38
+ dependency ff:webserver:0.0.0
39
+
40
+ // Runs on the server
41
+ nodeMain(system: NodeSystem) {...}
42
+
43
+ // Runs in the browser
44
+ browserMain(system: BrowserSystem) {...}
45
+
46
+ // Runs at build time
47
+ buildMain(system: BuildSystem) {...}
48
+ """, firefly = True)
49
+ )
50
+ SplitSection(
51
+ "Concise type definitions"
52
+ Paragraph([
53
+ Text("Model your types in a brief format that fits multiple definitions on one screen.")
54
+ Text("Be precise about whether things can be missing with")
55
+ Code("Option[T]", firefly = True)
56
+ Text("and make invalid states unrepresentable with variants and type parameters.")
57
+ ])
58
+ CodeBlock("""
59
+ data User(
60
+ id: UserId
61
+ name: String
62
+ email: Option[String]
63
+ )
64
+
65
+ data Tree[T] {
66
+ Leaf(value: T)
67
+ Branch(left: Tree[T], right: Tree[T])
68
+ }
69
+ """, firefly = True)
70
+ )
71
+ SplitSection(
72
+ "Convenient collections"
73
+ Paragraph([
74
+ Text("Immutable and mutable collections are part of the standard library.")
75
+ Text("Maps, sets, arrays, lists and streams come with a rich set of methods that you can use to write code that's instantly clear to the reader.")
76
+ ])
77
+ CodeBlock("""
78
+ let emails = houses
79
+ .flatMap {_.owners}
80
+ .map {_.email}
81
+ .filter {!_.endsWith("example.com")}
82
+ .toSet()
83
+
84
+ emails.each {email =>
85
+ sendNeighborhoodNewsletter(email)
86
+ }
87
+ """, firefly = True)
88
+ )
89
+ SplitSection(
90
+ "Deep pattern matching"
91
+ Paragraph([
92
+ Text("You can directly pattern match on function arguments, even in lambda functions.")
93
+ Text("The compiler checks for exhaustiveness, ensuring that all possible cases are covered.")
94
+ Text("Pattern guards are supported, so you can extract things with arbitrary logic.")
95
+ ])
96
+ CodeBlock("""
97
+ blockElements.map {
98
+ | Paragraph(inlines) =>
99
+ renderParagraph(inlines)
100
+ | Code(code, Some(language)) =>
101
+ renderHighlighted(code, language)
102
+ | Code(code, None) =>
103
+ renderCode(code)
104
+ | Video(url) {vimeoId(url) | Some(id)} =>
105
+ renderVimeo(id)
106
+ | Video(url) =>
107
+ renderVideo(url)
108
+ }
109
+ """, firefly = True)
110
+ )
111
+ SplitSection(
112
+ "Edit time error detection"
113
+ Paragraph([
114
+ Text("In Firefly, a large class of errors is detected by the IDE as you type.")
115
+ Text("You can usually fix these without even reading the error message.")
116
+ Text("And when you need to read the error messages, they're short and to the point.")
117
+ ])
118
+ Image("/assets/edit-time-error.png")
119
+ )
120
+ SplitSection(
121
+ "Type driven autocompletion"
122
+ Paragraph([
123
+ Text("The language server comes with type driven autocompletion.")
124
+ Text("It instantly presents a very precise list of completions, and the expected type is used to preselect a likely completion.")
125
+ ])
126
+ Image("/assets/autocomplete-small.png")
127
+ )
128
+ SplitSection(
129
+ "No async dilemma"
130
+ Paragraph([
131
+ Text("In Firefly, there's no")
132
+ Code("async")
133
+ Text("or")
134
+ Code("await")
135
+ Text("syntax.")
136
+ Text("Instead, the compiler infers which calls are asynchronous and automatically generates the appropriate code.")
137
+ Text("A method like")
138
+ Code(".map")
139
+ Text("on lists is called asynchronously only when the lambda function you pass is asynchronous.")
140
+ ])
141
+ CodeBlock("""
142
+ let files = ["a.txt", "b.txt"]
143
+ // async .map call
144
+ let contents = files.map {file =>
145
+ system.path(file).readText()
146
+ }
147
+ // sync .map call
148
+ let upper = contents.map {content =>
149
+ content.upper()
150
+ }
151
+ """, firefly = True)
152
+ )
153
+ SplitSection(
154
+ "No hidden I/O"
155
+ Paragraph([
156
+ Text("The main function is passed a")
157
+ Code("system")
158
+ Text("argument that represents all the I/O you can do.")
159
+ Text("It's a plain object, and you can simply wrap it to create a new")
160
+ Text("object with less capabilities. You can tell what effects a top level function")
161
+ Text("can have simply by looking at what arguments it receives.")
162
+ ])
163
+ CodeBlock("""
164
+ nodeMain(system: NodeSytem) {
165
+ let html = fetchSite(system.httpClient())
166
+ system.writeLine(html)
167
+ }
168
+
169
+ // this function can only do HTTP requests
170
+ fetchSite(httpClient: HttpClient): String {
171
+ let url = "https://www.example.com/"
172
+ httpClient.get(url, []) {_.readText()}
173
+ }
174
+ """, firefly = True)
175
+ )
176
+ SplitSection(
177
+ "Structural equality"
178
+ Paragraph([
179
+ Text("Traits are used for equality, ordering etc., and the core traits are automatically implemented for")
180
+ Code("data")
181
+ Text("types if you don't supply your own implementation.")
182
+ Text("They make")
183
+ Code("==")
184
+ Text("and")
185
+ Code("<")
186
+ Text("type safe, unlike in most languages, and")
187
+ Text("they're a lot simpler than the traits you find in Rust.")
188
+ ])
189
+ CodeBlock("""
190
+ trait T: Order {
191
+ compare(x: T, y: T): Ordering
192
+ }
193
+
194
+ instance Bool: Order {
195
+ compare(x: Bool, y: Bool): Ordering {
196
+ | False, True => OrderingBefore
197
+ | True, False => OrderingAfter
198
+ | _, _ => OrderingSame
199
+ }
200
+ }
201
+ """, firefly = True)
202
+ )
203
+ /*
204
+ SplitSection(
205
+ "Batteries included"
206
+ Paragraph([
207
+ Text("The standard library contains functionality that's required by most applications.")
208
+ Text("Apart from what you've seen above, there's also the following modules.")
209
+ ])
210
+ Bullets([
211
+ [Bold("Random:"), Text("Pseudorandom numbers")]
212
+ [Bold("Instant:"), Text("UTC timestamps")]
213
+ [Bold("Json:"), Text("JSON support")]
214
+ [Bold("Buffer:"), Text("Binary data")]
215
+ [Bold("Stream:"), Text("Streams (sync or async)")]
216
+ [Bold("Task:"), Text("Structured concurrency")]
217
+ [Bold("Log:"), Text("Simple logging")]
218
+ ])
219
+ )
220
+ */
221
+ Section("The Firefly Stack", [
222
+ Paragraph([
223
+ Bold("The Firefly Stack is a set of packages for building webapps.")
224
+ Text("The packages are maintained by the developers of Firefly.")
225
+ Text("You can use them individually or together.")
226
+ Italic("Here's what you can do with that.")
227
+ ])
228
+ ])
229
+ SplitSection(
230
+ "Interactive webapps"
231
+ Paragraph([
232
+ Text("The")
233
+ Code("ff:lux")
234
+ Text("package provides a declarative DOM framework for building highly interactive webapps.")
235
+ Text("Tasks that belong to removed nodes are automatically cancelled.")
236
+ Text("All without any virtual DOM.")
237
+ ])
238
+ CodeBlock("""
239
+ lux.useState(0): count, setCount =>
240
+ lux.button {
241
+ lux.text("Clicked " + count + " times")
242
+ lux.onClick {event =>
243
+ event.preventDefault()
244
+ setCount(count + 1)
245
+ }
246
+ }
247
+ """, firefly = True)
248
+ )
249
+ SplitSection(
250
+ "Type safe RPC"
251
+ Paragraph([
252
+ Text("All your custom")
253
+ Code("data")
254
+ Text("types are automatically serializable in Firefly.")
255
+ Text("With ")
256
+ Code("ff:rpc")
257
+ Text("you can set up type safe remote procedure calls from the browser to the webserver or between services.")
258
+ Text("You can even")
259
+ Italic("go to definition")
260
+ Text("across RPC boundaries.")
261
+ ])
262
+ CodeBlock("""
263
+ data Message(text: String)
264
+ instance Message: Rpc[Int]
265
+
266
+ // the browser sends a Message
267
+ let client = Rpc.newClient(...)
268
+ let messageId = client.call(Message("Hello"))
269
+
270
+ // the webserver replies with an Int
271
+ let server = Rpc.newServer(...)
272
+ server.add {| context, Message(text) => 42 }
273
+ """, firefly = True)
274
+ )
275
+ SplitSection(
276
+ "WebSocket server & client"
277
+ Paragraph([
278
+ Text("The ")
279
+ Code("ff:webserver")
280
+ Text("package comes with WebSocket support.")
281
+ Text("In just a few lines of code, you can start serving WebSockets.")
282
+ Text("The ")
283
+ Code("ff:websocket")
284
+ Text("package allows you to connect to any WebSocket server.")
285
+ Text("If both ends are running Firefly, you can use the built-in binary serialization.")
286
+ ])
287
+ CodeBlock("""
288
+ let server = WebServer.new(system, host, port)
289
+ server.enableWebSockets()
290
+
291
+ server.listen {request =>
292
+ let ws = request.openWebSocket()
293
+ ws.subscribe("chat")
294
+ while {True} {
295
+ let message = ws.readText().grab()
296
+ ws.publishText("chat", message)
297
+ }
298
+ }
299
+ """.replace("'''", "\"\"\""), firefly = True)
300
+ )
301
+ SplitSection(
302
+ "Database pooling"
303
+ Paragraph([
304
+ Text("The")
305
+ Code("ff:postgresql")
306
+ Text("package lets you create a connection pool for a PostgreSQL database.")
307
+ Text("You can then execute transactions and build stateful applications.")
308
+ ])
309
+ CodeBlock("""
310
+ let pool = Pg.newPool(...)
311
+
312
+ let emails = pool.transaction {connection =>
313
+ connection
314
+ .statement('''
315
+ select email from users
316
+ where id <= $maxId
317
+ ''')
318
+ .withInt("maxId", 100)
319
+ .map {_.getString("email").grab()}
320
+ }
321
+ """.replace("'''", "\"\"\""), firefly = True)
322
+ )
323
+ Section("What Firefly doesn't have", [
324
+ Bullets([
325
+ [
326
+ Bold("No function overloading.")
327
+ Text("Overloading leads to uninformative \"No matching overload\" errors.")
328
+ ]
329
+ [
330
+ Bold("No implicit casts.")
331
+ Text("Ever recieved an email addressed to \"Dear None\"? Static types fail to prevent this if you can implicitly cast an Option[T] to a String.")
332
+ ]
333
+ [
334
+ Bold("No dynamic typing.")
335
+ Text("Dynamic typing causes runtime errors and encourages hidden and underspecified contracts that makes refactoring harder than it needs to be.")
336
+ ]
337
+ [
338
+ Bold("No nulls.")
339
+ Text("Eliminating null ensures types accurately represent known values, reducing runtime errors and redundant checks.")
340
+ ]
341
+ [
342
+ Bold("No subtyping.")
343
+ Text("Subtyping introduces complexity via type bounds and variance. It also allows mixing loosely related types, promoting incomplete runtime type checks.")
344
+ ]
345
+ [
346
+ Bold("No inheritance.")
347
+ Text("Inheritance can lead to overgeneralization and scattering of business logic, making it hard to get the full picture at any level.")
348
+ ]
349
+ [
350
+ Bold("No macros.")
351
+ Text("Macros let you define whole new languages, that are typically wildly undertooled compared to the base language.")
352
+ ]
353
+ [
354
+ Bold("No type level programming.")
355
+ Text("Type level programming leads to inscrutable function signatures and hard to understand error messages.")
356
+ ]
357
+ ])
358
+ ])
359
+ ])
360
+ }