";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))
Unquoted version: Try it on codingground.
Quoted version: Try it on codingground.
Note that the output looks something like this
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
because the code is interpreted declaration by declaration (each ;
ends a declaration) and shows the value and type of each declaration.
Background
In SML there is a quine of the form <code>"<code in quotes>"
:
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
and one in the form "<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Both rely on the fact that the <code>
-part contains no quotes and can thus be quoted with out the need for escaping anything, the "
needed to output the quine are given by str(chr 34)
.
They also heavily rely on the implicit identifier it
which is used when no explicit identifier is given in a declaration.
In the first quine str(chr 34);
binds it
to the string containing "
, fn x=>
starts an anonymous function taking one argument x
, then concatenates x^it^x^it
and prints the resulting string. This anonymous function is directly applied to a string containing the program code, so the concatenation x^it^x^it
yields <code>"<code>"
.
The second quine starts with just the program code as string ";str(chr 34)^it;print(it^it)";
which is bound to it
. Then str(chr 34)^it;
concatenates a quote to the start of the string and as again no explicit identifier is given, the resulting string "<code>
is bound to it
. Finally print(it^it)
concatenates the string with itself yielding "<code>"<code>
which is then printed.
Explanation
Edit: No longer up to date with the 108-byte version, however one might understand it too after reading this explanation.
The quote-safe quine combines both of the above approaches and is itself of the form "<code>"<code>
. Putting this in again in quotes yields ""<code>"<code>"
, so we get an empty string and then a quine of the other form.
That means the program is either given its own source in the form "<code>
by the identifier it
, or it
is just "
and we are given our own source <code>
as argument and must thus be a function which handles such an argument.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
To identify in which case we are, we check whether the size of it
is larger than 1. If not then it
is "
and we are in the second case, so the else
-part returns an anonymous function fn x=>print(it^it^x^it^x^it)
which is then called because its followed by source as string. Note the leading it^it^
which is needed for the empty string at the start of the of the program.
If size it
is larger than 1 we are in the then
-part and just perform print(it^it)
, right? Not quite, because I neglected to tell you that SML is strongly typed which means that a conditional if <cond> then <exp_1> else <exp_2>
must always have the same type which again means that the expressions <exp_1>
and <exp_2>
need to have the same type. We already know the type of the else
part: An anonymous function which takes a string and then calls print
has type string -> <return type of print>
, and print
has type string -> unit
(unit
is in some way similar to void
in other languages), so the resulting type is again string -> unit
.
So if the then
part was just print(it^it)
which has type unit
, we would get a type mismatch error. So how about fn _=>print(it^it)
? (_
is a wildcard for an argument that is not used) This anonymous function on its own has type 'a -> unit
where 'a
stands for an arbitrary type, so in the context of our conditional which enforces a string -> unit
type this would work. (The type variable 'a
gets instantiated with type string
.) However, in this case we would not print anything as the anonymous function is never called! Remember, when we go in the then
-part the overall code is "<code>"<code>
, so the <code>
-part evaluates to a function but, as nothing comes after it, its not called.
Instead we use a sequentialisation which has the form (<exp_1>; ...; <exp_n>)
where <exp_1>
to <exp_n-1>
may have arbitrary types and the type of <exp_n>
provides the type of the whole sequentialisation. From a functional point of view the values of <exp_1>
to <exp_n-1>
are simply discarded, however SML also supports imperative constructs so the expressions may have side effects. In short, we take (print(it^it);print)
as the then
-part, thus printing first and then returning the function print
which has the correct type.
"
? Some languages support two or three quote characters. \$\endgroup\$