How to build a quine, reprise
Posted on
Let's revisit the quine building exercise described in the first post in this series, this time in F#. Small issues will arise, and we'll see how to handle them.
Step One
As before, we start with an empty list, and three print loops.
let data = [
]
for d in data do
System.Console.WriteLine(d)
for d in data do
System.Console.WriteLine(d)
for d in data do
System.Console.WriteLine(d)
Notice that the closing bracket is indented one space. The indentation is required in F#.
Step Two
As before, the middle print loop is special. Let's set it up. The variable space is used for indentation, and semicolon is the element separator. Recall that python used a comma for that.
let data = [
]
let q = string(char 34)
let semicolon = string(char 59)
let space = string(char 32)
for d in data do
System.Console.WriteLine(d])
for d in data do
System.Console.WriteLine(space + q + d + q + semicolon)
for d in data do
System.Console.WriteLine(d)
Step Three
We need to put some content into data, via copy-paste:
- Copy the program so far.
- Paste it on a new line following the first bracket delimiting data.
- Wrap each line in quotes, and append a semicolon to each pasted line. A semicolon, not a comma.
- Indent each line by one space.
Here's the result
let data = [
"let data = [";
" ]";
"let q = string(char 34)";
"let semicolon = string(char 59)";
"let space = string(char 32)";
"for d in data do";
" System.Console.WriteLine(d)";
"for d in data do";
" System.Console.WriteLine(space + q + d + q + semicolon)";
"for d in data do";
" System.Console.WriteLine(d)";
]
let q = string(char 34)
let semicolon = string(char 59)
let space = string(char 32)
for d in data do
System.Console.WriteLine(d)
for d in data do
System.Console.WriteLine(space + q + d + q + semicolon)
for d in data do
System.Console.WriteLine(d)
Step Four
Next we adjust the print loops and their string representations inside data. We have to be careful. Blindly copying the approach used in the earlier post will not suffice. Different programming languages use different conventions that may become relevant. For instance:
- What is the element separator character in a list/array/vector?
- Do lists/arrays/vectors allow the use of trailing separators?
- Does the language count from 0 (e.g. python) or from 1 (e.g. lua)?
- Do numerical ranges include or exclude the upper limit?
In python data[:b] does not include the bth element, but in F# data[..b] does include it, so we have to adjust for that. In python the 1st and 3rd print loops ranged over data[:boundary] and data[boundary:], a pleasant symmetry. F# requires data[..boundary-1] and data[boundary..] to achieve the same outcome. We'll see more minor linguistic differences in the next post, when the ruby, lua, and babashka languages join the party.
Here's the final result.
let data = [
"let data = [";
" ]";
"let q = string(char 34)";
"let semicolon = string(char 59)";
"let space = string(char 32)";
"let boundary = 1";
"for d in data[..boundary-1] do";
" System.Console.WriteLine(d)";
"for d in data do";
" System.Console.WriteLine(space + q + d + q + semicolon)";
"for d in data[boundary..] do";
" System.Console.WriteLine(d)";
]
let q = string(char 34)
let semicolon = string(char 59)
let space = string(char 32)
let boundary = 1
for d in data[..boundary-1] do
System.Console.WriteLine(d)
for d in data do
System.Console.WriteLine(space + q + d + q + semicolon)
for d in data[boundary..] do
System.Console.WriteLine(d)
Testing
Save the program as quine.fsx and run a diff via zsh process substitution:
❯ dotnet fsi --version
Microsoft (R) F# Interactive version 12.0.0.0 for F# 6.0
❯ diff -s quine.fsx =(dotnet fsi quine.fsx)
Files quine.fsx and /tmp/zshsHJ5T3 are identical
If you want to try this at home, make sure to call your program quine.fsx rather than quine.fs. The F# compiler considers an fs file to be program and an fsx file to be a script; it treats them differently. For our purpose the script is easier to work with.
But wait, there's more.
In the next post we introduce more languages and automate the building of the quines. This will set the stage for building quine relays in the subsequent post.
The supporting code repo is drcabana/quines.