-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathHome.jsx
More file actions
1420 lines (1270 loc) · 61.2 KB
/
Home.jsx
File metadata and controls
1420 lines (1270 loc) · 61.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import { useEffect } from 'react';
import {
Card,
CardBody,
CardImg,
CardText,
CardTitle,
Col,
Container,
Row,
UncontrolledCarousel
} from 'reactstrap';
import InlineEditor from "../util/InlineEditor";
const carousel = [
{
src: '/images/vscode1.png',
caption: 'Slide 1',
header: 'Semantic Highlighting',
},
{
src: '/images/vscode2.png',
caption: 'Slide 2',
header: 'Hover to Inspect',
},
{
src: '/images/vscode3.png',
caption: 'Slide 3',
header: 'Highlight References',
},
{
src: '/images/vscode4.png',
caption: 'Slide 4',
header: 'Inline Errors',
}
];
function Home() {
useEffect(() => {
document.title = "The Flix Programming Language";
}, []);
return (
<Container>
<Row className="mb-3">
<Col md="6">
<h1>Flix —</h1>
<h2 className="motto">
A powerful <span className="font-weight-bold text-success">effect-oriented</span> programming language
</h2>
<p className="text-justify">
Flix is a principled effect-oriented functional,
imperative, and logic programming language developed at <a
href="https://cs.au.dk/">Aarhus University</a> and by a community of <a
href="https://github.com/flix/flix/graphs/contributors">open source contributors</a>.
</p>
<h4>Why effect-oriented? And why Flix?</h4>
<p className="text-justify">
<span className="font-italic">Why Effects?</span> Effect systems represent the next major
evolution in statically typed programming languages. By explicitly modeling side effects,
effect-oriented programming enforces modularity and helps program reasoning. User-defined
effects and handlers allow programmers to implement their own control structures.
</p>
<p className="text-justify">
<span className="font-italic">Why Flix?</span> We claim that of all the upcoming
effect-oriented programming languages, Flix offers the most <span
className="font-weight-bold">complete language implementation</span>, the most <span
className="font-weight-bold">extensive standard library</span>, the most <span
className="font-weight-bold">detailed documentation</span>, and the <span
className="font-weight-bold">best tool support.</span>
</p>
<p className="text-justify">
Moreover, Flix builds on proven programming language technology, including: <span
className="font-weight-bold">algebraic data types and pattern matching</span>, <span
className="font-weight-bold">extensible records</span>, <span
className="font-weight-bold">traits</span>, <span
className="font-weight-bold">higher-kinded types</span>, <span
className="font-weight-bold">associated types and effects</span>, <span
className="font-weight-bold">structured concurrency</span>,
and more.
</p>
</Col>
<Col md="6">
<InlineEditor>{`/// Demonstrates composing multiple HTTP middleware
/// via \`with\` clauses. Stacks base URL, default
/// headers, retry, circuit breaker, and logging.
/// Each \`with\` wraps the
/// preceding block. The \`Http\` and \`Logger\` effects
/// propagate to \`main\` and are handled automatically
/// via their default handlers. Relative paths are
/// resolved against the base URL; absolute URLs
/// bypass it.
def main(): Unit \\ { Clock, Http, Logger, IO } =
let defaultHeaders = Map#{
"Accept" => List#{"application/json"},
"Authorization" => List#{"Bearer tok123"}
};
run {
let urls = List#{"/api/users", "/api/posts"};
foreach (url <- urls) {
match Http.get(url) {
case Ok(res) => println("\${url} -> \${status(res)}")
case Err(err) => println("\${url} -> \${err}")
}
};
match Http.get("https://notfound.flix.dev/") {
case Ok(res) => println("notfound -> \${status(res)}")
case Err(err) => println("notfound -> \${err}")
}
} with Http.withBaseUrl("https://flix.dev")
with Http.withDefaultHeaders(defaultHeaders)
with Http.withRetry(
Retry.linear(maxRetries = 2, delay = milliseconds(100)))
with Http.withCircuitBreaker(
failureThreshold = 3, cooldown = seconds(5))
with Http.withLogging`}</InlineEditor>
</Col>
</Row>
<hr className="mb-3"/>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Algebraic Data Types and Pattern Matching</h4></CardTitle>
<CardText>
<p>
Algebraic data types and pattern matching are the bread-and-butter of functional
programming and are supported by Flix with minimal fuss.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`enum Shape {
case Circle(Int32),
case Square(Int32),
case Rectangle(Int32, Int32)
}
def area(s: Shape): Int32 = match s {
case Circle(r) => 3 * (r * r)
case Square(w) => w * w
case Rectangle(h, w) => h * w
}`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`def origin(): (Int32, Int32) = (0, 0)
def oneByOne(): {w = Int32, h = Int32} = {w = 1, h = 1}
def twoByFour(): {w = Int32, h = Int32} = {w = 2, h = 4}
def area(rect: {w = Int32, h = Int32 | r}): Int32 =
rect#w * rect#h
def f(): Int32 = area({h = 1, color = "Blue", w = 2})`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Tuples and Records</h4></CardTitle>
<CardText>
<p>
Flix has built-in support for tuples and records.
</p>
<p>
Records use structural typing and are extensible.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Purity and Impurity</h4></CardTitle>
<CardText>
<p>
Flix precisely tracks the purity of every expression in a program.
</p>
<p>
The Flix compiler provides an ironclad guarantee that if an expression is pure
then it cannot have side-effects and it is referentially transparent.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`/// A pure function is annotated with \`\\ {}\`.
def inc1(x: Int32): Int32 \\ {} = x + 1
/// An impure function is annotated with \`\\ IO\`.
def inc2(x: Int32): Int32 \\ IO =
println("x = \${x}");
x + 1
def f(): Int32 \\ IO = // f is impure
let r1 = inc1(123); // pure
let r2 = inc2(456); // impure
r1 + r2 // pure`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`///
/// The purity of \`map\` depends on the purity of \`f\`.
///
def map(f: a -> b \\ ef, l: List[a]): List[b] \\ ef =
match l {
case Nil => Nil
case x :: xs => f(x) :: map(f, xs)
}
`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Polymorphic Effects</h4></CardTitle>
<CardText>
<p>
Flix is able to track purity through higher-order effect polymorphic functions.
</p>
<p>
For example, Flix knows that the purity of <code>List.map</code> depends on the
purity of its function argument <code>f</code>.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Algebraic Effects</h4></CardTitle>
<CardText>
<p>
Flix supports algebraic effects, i.e. user-defined effects and handlers.
In particular, Flix supports multi-shot resumptions.
</p>
<p>
Effect-oriented programming, with algebraic effects, allow programmers to
write pure functions modulo effects. Effect handlers enable program reasoning,
modularity, and testability.
</p>
<p>
For example, the program on the right expresses a <code>greeting</code> function
that is pure modulo the current time of the day. In <code>main</code> we call
the function and handle the <code>HourOfDay</code> effect by getting the
real-world time from Java's <code>LocalDateTime</code>.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`import java.time.LocalDateTime
eff HourOfDay {
def getCurrentHour(): Int32
}
def greeting(): String \\ {HourOfDay} =
let h = HourOfDay.getCurrentHour();
if (h <= 12) "Good morning"
else if (h <= 18) "Good afternoon"
else "Good evening"
def main(): Unit \\ IO =
run {
println(greeting())
} with handler HourOfDay {
def getCurrentHour(_, resume) =
let dt = LocalDateTime.now();
resume(dt.getHour())
}
`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Region-based Local Mutation</h4></CardTitle>
<CardText>
<p>
Flix supports region-based local mutation, which makes it possible to
implement <i>pure</i> functions that internally uses mutable state and
destructive operations, as long as these operations are confined to the region.
</p>
<p>
We can use local mutation when it is more natural to write a function using
mutable data and in a familiar imperative-style while still remaining pure
to the outside world.
</p>
<p>
We can also use local mutation when it is more efficient to use mutable
data structures, e.g. when implementing a sorting algorithm.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`///
/// We can implement a *pure* \`sort\` function which
/// internally converts an immutable list to an array,
/// sorts the array in-place, and then converts it
/// back to an immutable list.
///
def sort(l: List[a]): List[a] \\ {} with Order[a] =
region r {
List.toArray(r,l) !> Array.sort! |> Array.toList
}
///
/// We can also write a *pure* \`toString\` function which
/// internally uses a mutable StringBuilder.
///
def toString(l: List[a]): String with ToString[a] =
region r {
let sb = StringBuilder.new(r);
foreach (x <- l) {
StringBuilder.appendString!("\${x} :: ", sb)
};
StringBuilder.appendString!("Nil", sb);
StringBuilder.toString(sb)
}
`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`///
/// We can inspect the purity of a function argument.
///
def inspect(f: a -> b \\ ef): Unit \\ IO =
reifyEff(f) {
case Pure(g) => println("f is pure")
case _ => println("f is not pure")
}
///
/// We can use purity information to safely switch between
/// lazy (or parallel) evaluation. In this case, if f is
/// pure then perform the map operation lazily.
///
def map(f: a -> b \\ ef, l: LazyList[a]): LazyList[b] \\ ef =
reifyEff(f) {
case Pure(g) => mapL(g, l)
case _ => mapE(f, l)
}`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Purity Reflection</h4></CardTitle>
<CardText>
<p>
Flix supports a meta-programming construct that enables higher-order functions
to inspect the purity of a function argument and use that information to vary
their behavior.
</p>
<p>
For example, the <code>DelayList.map</code> function varies its behavior between
eager and lazy evaluation depending on the purity of its function argument.
</p>
<p>
We can exploit purity reflection to selectively use lazy or parallel
evaluation inside a library without changing the semantics from the
point-of-view of the clients.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Parallelism</h4></CardTitle>
<CardText>
<p>
Flix makes it simple and easy to evaluate <i>pure</i> code in parallel.
</p>
<p>
For example, the code on the right shows a parallel implementation of
the <code>List.map</code> function using the <code>par</code> construct.
</p>
<p>
Internally, the <code>par</code> construct uses
light-weight <code>VirtualThread</code>s.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`///
/// A parallel implementation of the List.map function.
///
def parMap(f: a -> b, l: List[a]): List[b] = match l {
case Nil => Nil
case x :: xs =>
// Evaluate f(x) and parMap(f, xs) in parallel.
par (r <- f(x); rs <- parMap(f, xs))
yield r :: rs
}`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`def main(): Unit \\ IO =
region rc {
// A channel which can buffer one message.
let (tx, rx) = Channel.buffered(rc, 1);
spawn say("Meow!", tx) @ rc; // thread 1
spawn say("Woof!", tx) @ rc; // thread 2
Channel.recv(rx) |> println
} // Execution is blocked until both threads finish.
/// Sends the string s on the given channel tx.
def say(s: String, tx: Sender[String, r]): Unit \\ r =
Channel.send(s, tx)
`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Structured Concurrency</h4></CardTitle>
<CardText>
<p>
Flix supports <a
href="https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/">structured
concurrency</a>.
</p>
<p>
For example, the code on the left shows the creation of a fresh
region named <code>rc</code> in which two threads are spawned.
</p>
<p>
Importantly, control-flow does not leave the region before <i>both</i> threads
have terminated. Hence the two threads cannot outlive the lifetime of their
enclosing region.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Traits</h4></CardTitle>
<CardText>
<p>
Flix uses traits to abstract over types that support a common set of
operations.
</p>
<p>
For example, the <code>Eq</code> trait captures the notion of equality
and is used throughout the standard library.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`trait Eq[a] {
def eq(x: a, y: a): Bool
def neq(x: a, y: a): Bool = not Eq.eq(x, y)
}
instance Eq[(a1, a2)] with Eq[a1], Eq[a2] {
def eq(t1: (a1, a2), t2: (a1, a2)): Bool =
let (x1, x2) = t1;
let (y1, y2) = t2;
x1 == y1 and x2 == y2
}`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`trait Foldable[t : Type -> Type] {
///
/// Left-associative fold of a structure.
///
def foldLeft(f: (b, a) -> b \\ ef, s: b, t: t[a]): b \\ ef
///
/// Right-associative fold of a structure.
///
def foldRight(f: (a, b) -> b \\ ef, s: b, t: t[a]): b \\ ef
}`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Higher-Kinded Types</h4></CardTitle>
<CardText>
<p>
Flix supports higher-kinded types making it possible to abstract over type
constructors. For example,
both <code>Option</code> and <code>List</code> implement <code>Foldable</code>.
</p>
<p>
The Flix standard library ships with many common traits, such
as <code>Monoid</code>, <code>Functor</code>, and <code>Foldable</code>.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Associated Types</h4></CardTitle>
<CardText>
<p>
Flix supports associated types, which allow the types in instance signatures
to depend on the instance type.
</p>
<p>
The code on the right defines a trait with an associated type <code>Elm</code>,
which enables each <code>Coll</code> instance to define its element type.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`trait Coll[a] {
/// The element type of the collection.
type Elm
/// Converts the collection to a list of its elements.
def toList(coll: a): List[Coll.Elm[a]]
}
instance Coll[Map[k, v]] {
type Elm = (k, v)
def toList(m: Map[k, v]): List[(k, v)] = ...
}
`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`trait Coll[a] {
/// The element type of the collection.
type Elm
/// The effect associated with the collection.
type Aef
/// Converts the collection to a list of its elements.
def toList(coll: a): List[Coll.Elm[a]] \\ Coll.Aef[a]
}
instance Coll[MutMap[k, v, r]] {
type Elm = (k, v)
type Aef = r
def toList(m: Map[k, v]): List[(k, v)] \\ r = ...
}
`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Associated Effects</h4></CardTitle>
<CardText>
<p>
Associated effects allow the effects in trait members to depend on the instance
type.
This makes it easy to create abstractions over both pure and effectful
operations,
and mutable and immutable data structures.
</p>
<p>
The code on the left adds an associated effect <code>Aef</code> to
the <code>Coll</code> trait,
which makes it possible to add instances for mutable collections.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Monadic For-Yield</h4></CardTitle>
<CardText>
<p>
Flix supports a monadic <code>forM</code>-yield construct similar to Scala's
<code>for</code>-comprehensions and Haskell's <code>do</code> notation.
The <code>forM</code> construct is syntactic sugar for uses
of <code>point</code> and <code>flatMap</code> (which are provided by
the <code>Monad</code> trait).
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`def divide(x: Int32, y: Int32): Option[Int32] =
if (y == 0) None else Some(x / y)
def f(): Option[Int32] =
forM (
x <- divide(5, 2);
y <- divide(x, 8);
z <- divide(9, y)
) yield x + y + z
`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<InlineEditor>
{`def validateUser(s: String): Validation[Err, String] = ...
def validatePass(s: String): Validation[Err, String] = ...
def conn(u: String, p: String): Validation[Err, Connection] =
forA (
user <- validateUser(u);
pass <- validatePass(p)
) yield Connection(user, pass)
`}
</InlineEditor>
</Col>
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Applicative For-Yield</h4></CardTitle>
<CardText>
<p>
In addition to the monadic <code>forM</code> expression, Flix supports an
applicative <code>forA</code> expression that builds on
the <code>Applicative</code> trait. The <code>forA</code> construct makes
it simple to write error-handling code which uses the <code>Validation[e,
t]</code> data type.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Seamless Java Interoperability</h4></CardTitle>
<CardText>
<p>
Flix supports seamless Java interoperability, making it possible to reuse code
from the Java Standard Library and the Java ecosystem, e.g., via Maven.
</p>
<p>
Java support includes object creation, method invocation, exceptions, and
class/interface extension.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`import java.io.File
import java.io.FileWriter
import java.io.IOException
def main(): Unit \\ IO =
let f = new File("foo.txt");
try {
let w = new FileWriter(f);
w.append("Hello World\\n");
w.close()
} catch {
case ex: IOException =>
println("Unable to write file")
}`}
</InlineEditor>
</Col>
</Row>
<Row className="mb-4">
<Col md="12">
<Card className="border-0">
<CardBody>
<CardTitle><h4>First-class Datalog Constraints</h4></CardTitle>
<CardText>
<p>
Another unique feature of Flix is its embedded Datalog support. Datalog, a
powerful logic programming language in its own right, makes it simple and
elegant to express many fixpoint problems (including various graph reachability
problems):
</p>
<InlineEditor>
{`def reachable(g: List[(String, Int32, String)], minSpeed: Int32): List[(String, String)] =
let facts = inject g into Road/3;
let rules = #{
Path(x, y) :- Road(x, maxSpeed, y), if maxSpeed >= minSpeed.
Path(x, z) :- Path(x, y), Road(y, maxSpeed, z), if maxSpeed >= minSpeed.
};
query facts, rules select (src, dst) from Path(src, dst) |> Foldable.toList`}
</InlineEditor>
<p>
Datalog constraints are <i>first-class</i> which means that they may be passed
to and returned from functions, stored in data structures, composed with other
Datalog constraints, and solved. This makes it possible to express families of
Datalog programs.
</p>
</CardText>
</CardBody>
</Card>
</Col>
</Row>
<Row className="mb-4">
<Col md="6">
<Card className="border-0">
<CardBody>
<CardTitle><h4>Datalog Enriched with Lattice Semantics</h4></CardTitle>
<CardText>
<p>
Flix supports Datalog constraints enriched with lattice semantics.
</p>
<p>
The program on the right computes the delivery date for a collection of
parts. Each part is assembled from a collection of sub-components with various
delivery dates. For example, a car depends on a chassis and an engine. To build
a car, we need to wait for the chassis and engine to assembled and then we can
assemble the car itself.
</p>
<p>
Note that parts may depend on sub-components that themselves may depend on other
sub-components. In other words, the problem is recursive.
</p>
<hr/>
<p>
Datalog constraints enriched with lattice semantics is one of the more
advanced features of Flix and requires some background knowledge of lattice
theory and fixpoints.
</p>
</CardText>
</CardBody>
</Card>
</Col>
<Col md="6">
<InlineEditor>
{`let p = #{
/// Parts and the components they depend on.
PartDepends("Car", "Chassis").
PartDepends("Car", "Engine").
PartDepends("Engine", "Piston").
PartDepends("Engine", "Ignition").
/// Time required to assemble a part from its components.
AssemblyTime("Car", 7).
AssemblyTime("Engine", 2).
/// Expected delivery date for certain components.
DeliveryDate("Chassis"; 2).
DeliveryDate("Piston"; 1).
DeliveryDate("Ignition"; 7).
/// A part is ready when it is delivered.
ReadyDate(part; date) :-
DeliveryDate(part; date).
/// Or when it can be assembled from its components.
ReadyDate(part; assemblyTime + componentDate) :-
PartDepends(part, component),
AssemblyTime(part, assemblyTime),
ReadyDate(component; componentDate).
};
// Computes the delivery date for each component.
let r = query p select (c, d) from ReadyDate(c; d)
`}
</InlineEditor>
</Col>
</Row>
<hr className="mb-4"/>
<Row className="mb-4">
<Col md="12">
<h2>Complete Feature List</h2>
</Col>
<Col md="4">
<ul>
<li>algebraic data types</li>
<li>pattern matching</li>
<li>first-class functions</li>
<li>extensible records</li>
<li>parametric polymorphism</li>
<li>traits (i.e. type classes)</li>
<li>higher-kinded types</li>
<li>associated types and effects</li>
</ul>
</Col>
<Col md="4">
<ul>
<li>effect polymorphism + subeffecting</li>
<li>purity reflection</li>
<li>CSP-style concurrency</li>
<li>buffered & unbuffered channels</li>
<li>first-class datalog constraints</li>
<li>seamless interoperability with Java</li>
<li>unboxed primitives</li>
<li>keyword-based syntax</li>
</ul>
</Col>
<Col md="4">
<ul>
<li>monadic forM expressions</li>
<li>applicative forA expressions</li>
<li>expressions holes</li>
<li>compilation to JVM bytecode</li>
<li>full tail call elimination</li>
<li>resilient compiler architecture</li>
<li>parallel compiler architecture</li>
<li>human friendly errors</li>
</ul>
</Col>
</Row>
<hr className="mb-4"/>
<Row className="mb-4">
<Col md={6}>
<h2>Standard Library with Batteries Included</h2>
<p>
Flix comes with a fully-featured Standard Library that offers access to more than <span
className="text-success font-weight-bold">3,100+</span> functions.
</p>
<p>
For example, the <code>List</code> module has more than 100 functions and
the <code>Foldable</code> trait has more than 47 functions.
</p>
<p>
The full library can be explored at: <a
href="https://api.flix.dev/">https://api.flix.dev/</a>
</p>
<p>
In addition, Flix also provides access to the entire Java ecosystem via Maven.
</p>
</Col>
<Col md={6}>
<Card>
<CardImg src="/images/standardLibrary.png" alt="Standard Library"/>
</Card>
</Col>
</Row>
<hr className="mb-4"/>
<Row className="mb-4">
<Col md={6}>
<h2>Modern Compiler Architecture</h2>
<p>
Flix features a modern compiler architecture which is <span
className="font-weight-bold text-success">resilient</span>, <span
className="font-weight-bold text-success">incremental</span>, and <span
className="font-weight-bold text-success">parallel</span>.
</p>
<p>
In Flix, every compiler phase is parallel. The plot on the right shows the speed-up of each
compiler phase when run on a 24 core machine.
</p>
<p>
In other words, Flix can take full advantage of modern hardware, leading to speed-ups of
between <b>5x – 7x</b> on multi-core machines.
</p>
<p>
Furthermore, the Flix compiler is incremental which leads to significant speed-ups when
recompiling code that has already been compiled in the same compiler instance.
</p>
</Col>
<Col md={6}>
<CardImg src="/images/speedupWithPar.png" alt="Parallel Speedup"/>
</Col>
</Row>
<hr className="mb-4"/>
<Row className="mb-4">
<Col md={12}>
<h2>Compiler Performance: The Raw Numbers</h2>
<p>
The following table illustrates the performance of the Flix compiler on an Apple M2 Pro with
a 10‑core CPU running on OpenJDK 21:
</p>
<Col md={6}>