val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]
Try it online!
For MLton, full SML programs are either expressions delimited and terminated by ;
(e.g. print"Hello";print"World";
) or declarations with the var
and fun
keywords (e.g. var _=print"Hello"var _=print"World"
) where _
is a wild card which could also be replaced by any variable name.
The first option is useless for pristine programming because ;
on its own is a valid program (which does nothing, but doesn't error either). The problem with the second approach is that declarations like var _=print"Hello"
can be shortened to just var _="Hello"
(or even var _=print
) because the declaration with var
works as long as the right-hand side is a valid SML expression or value (SML is a functional language, so functions can be used as values too).
At this point, I was ready to declare pristine programming in SML impossible, when by chance I stumbled upon pattern matching in val
-declarations. It turns out that the syntax for declarations is not val <variable_name> = <expression>
but val <pattern> = <expression>
, where a pattern can consist of variable names, constants and constructors. As the print
function has type string -> unit
, we can use a pattern match on the unit
-value ()
to enforce that the print function is actually applied to the string: val()=print"Hey"
. With this approach, removing either print
or "Hey"
results in a Pattern and expression disagree
-error.
With this way of pristine printing at hand, the next step is to write a quine, before finally some more save-guarding needs to be added. I previously used an easy SML quine technique (see the revision history), but Anders Kaseorg pointed out a different approach which can save some bytes in his case. It uses the built-in String.toString
function to handle string escaping and is of the general form <code>"<data>"
, where "<data>"
is an escaped string of the code
before:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
This is a working quine but not yet pristine. First of all Anders Kaseorg found out that MLton accepts a single quote "
as code without producing errors, which means we cannot have code ending in a quote as above. The shortest way to prevent this would be to wrap everything after val()=
in a pair of parenthesis, however then the code could be reduced to val()=()
. The second shortest way I found is to use val()=hd[ ... ]
, that is we wrap everything into a list and return its first element to make the type checker happy.
To make sure that no part of the data string can be removed without being noticed, the pattern-matching in val
-declarations comes in handy again: The length of the final string to be printed (and thus the program length) should equal 195, so we can write let val t=... val 195=size t in print t end
in the body of the fn
abstraction instead of print(...)
. Removing a part of the string results in a length less than 189, thus causing a Bind
exception to be thrown.
There is still an issue left: the whole val 195=size t
check could simply be dropped. We can prevent this by expanding the check to match on a tuple: val t=... val(216,u)=(n+size t,t)in print u end
, such that removing the check results in an unbound variable u
.
Altogether, this yields the following 195 byte solution:
val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]
Applying the golfing trick of using operator variable names like !
, $
and %
instead of n
, t
and u
in order to save some white space (see this tip) leads to the final 182 byte version.
All other substring-removals which where not explicitly stated in the explanation should result in a syntax or type error.
Edit 1: length(explode t)
is just size t
.
Edit 2: Thanks to Anders Kaseorg for a different quine approach and pointing out a "vulnerability".
I'm assuming languages that don't error can't compete? – ATaco – 2017-07-11T05:25:09.630
@ATaco Unfortunately yes. Other languages, such as lisp, have the syntax structured in such a way that making a useful pristine program is impossible. – Shelvacu – 2017-07-11T05:28:47.770
RIP Actually/Seriously – ATaco – 2017-07-11T05:29:43.713
'Shortest answer in bytes for each language wins.' I am not sure about short being the best measure for a pristine program. – P. Siehr – 2017-07-11T07:14:39.007
@P.Siehr What would you reccomend instead? – Shelvacu – 2017-07-11T07:16:45.340
Difficult question. I have thought about it for some time after I read your challenge. If you see the measure as something that measures the difficulty or complexity of the answer, than it would grow proportional to length (longest) and not inversely proportional (shortest). Then again, that is contrary to 'shortest quine'. – P. Siehr – 2017-07-11T07:24:08.967
I'd actually say the longest pristine program is a better accomplishment. – Draconis – 2017-07-18T00:35:22.667