A quine in Flix
Posted on
One of my standard exercises when learning a new programming language is to write a quine. Here is a Flix language quine.
1 def quine(): Unit\
2 IO = {
3 let xs:List[String] = List#{
4 "def quine(): Unit",
5 " IO = {",
6 " let xs:List[String] = List#{",
7 " };",
8 " let join = String.flatten;",
9 " let get = List.slice;",
10 " let len = List.length;",
11 " let cp2v = c ->",
12 " match CodePoint.toChars(c) {",
13 " case Some(v) => v",
14 " case None => Vector#{}};",
15 " let st = x ->",
16 " cp2v(x) |> Vector.get(0) |> Char.toString;",
17 " let (qte, comma, bslash, space) =",
18 " (st(0x0022), st(0x002C), st(0x005C), st(0x0020));",
19 " let pad = join(space::space::space::space::Nil);",
20 " let quote = x ->",
21 " join(pad::pad::qte::x::qte::Nil);",
22 " let quoteComma =",
23 " x -> join(pad::pad::qte::x::qte::comma::Nil);",
24 " let firstLine =",
25 " join(get({start=0}, {end=1}, xs) ::: bslash::Nil);",
26 " firstLine |> println;",
27 " foreach(",
28 " line <- get({start=1}, {end=3}, xs))",
29 " line |> println;",
30 " foreach(",
31 " line <- get({start=0},{end=len(xs)-1}, xs))",
32 " quoteComma(line) |> println;",
33 " foreach(",
34 " line <- get({start=len(xs)-1},{end=len(xs)}, xs))",
35 " quote(line) |> println;",
36 " foreach(",
37 " line <- get({start=3}, {end=len(xs)}, xs))",
38 " line |> println",
39 "}"
40 };
41 let join = String.flatten;
42 let get = List.slice;
43 let len = List.length;
44 let cp2v = c ->
45 match CodePoint.toChars(c) {
46 case Some(v) => v
47 case None => Vector#{}};
48 let st = x ->
49 cp2v(x) |> Vector.get(0) |> Char.toString;
50 let (qte, comma, bslash, space) =
51 (st(0x0022), st(0x002C), st(0x005C), st(0x0020));
52 let pad = join(space::space::space::space::Nil);
53 let quote = x ->
54 join(pad::pad::qte::x::qte::Nil);
55 let quoteComma =
56 x -> join(pad::pad::qte::x::qte::comma::Nil);
57 let firstLine =
58 join(get({start=0}, {end=1}, xs) ::: bslash::Nil);
59 firstLine |> println;
60 foreach(
61 line <- get({start=1}, {end=3}, xs))
62 line |> println;
63 foreach(
64 line <- get({start=0},{end=len(xs)-1}, xs))
65 quoteComma(line) |> println;
66 foreach(
67 line <- get({start=len(xs)-1},{end=len(xs)}, xs))
68 quote(line) |> println;
69 foreach(
70 line <- get({start=3}, {end=len(xs)}, xs))
71 line |> println
72 }
If you want to run this code you likely want to grab it as a gist.
The code follows the quine generation approach I describe here and here, adapted to the syntax of the Flix language.
I did not put any comments in the code itself, but some remarks are in order:
- I use short names to help keep the individual lines short. This is the reason for the definitions in lines 41-43.
- Notice that no double quote characters appear anywhere except to denote string literals in the xs data structure. This is purpose of lines 44-51.
- Flix does not currently allow trailing commas in lists; lines 66-68 are needed to handle that special case.
- Flix strings do not allow the use of the unescaped backslash character. That character is used in the first line of the program as part of the syntax for declaring the IO effect. Lines 57-59 handle printing that first line.
- I suspect a much cleverer and shorter Flix quine is possible. But brevity was not my goal, for reasons explained here.
- If the syntax highlighting above looks wonky, that's because it is. The highlight software I'm using does not (yet) support Flix, so I told it to highlight the code as Haskell.