15
\$\begingroup\$

This challenge is based off of Helka Homba's question Programming a Pristine World. From that question, the definition of a pristine program is:

Let's define a pristine program as a program that does not have any errors itself but will error if you modify it by removing any contiguous substring of N characters, where 1 <= N < program length.

For example, the three character Python 2 program

`8`

is a pristine program (thanks, Sp) because all the programs resulting from removing substrings of length 1 cause errors (syntax errors in fact, but any type of error will do):

8`
``
`8

and also all the programs resulting from removing substrings of length 2 cause errors:

`
`

If, for example, `8 had been a non-erroring program then `8` would not be pristine because all the results of substring removal must error.

Notes:

  • Compiler warnings do not count as errors.
  • The erroring subprograms can take input or give output or do anything else as long as they error no matter what eventually.

Your task is to create a program of non-zero length that prints its own source code exactly, follows the rules for a proper quine, and is pristine.

Shortest answer in bytes for each language wins.

\$\endgroup\$
  • \$\begingroup\$ I'm assuming languages that don't error can't compete? \$\endgroup\$ – ATaco Jul 11 '17 at 5:25
  • \$\begingroup\$ @ATaco Unfortunately yes. Other languages, such as lisp, have the syntax structured in such a way that making a useful pristine program is impossible. \$\endgroup\$ – Shelvacu Jul 11 '17 at 5:28
  • \$\begingroup\$ RIP Actually/Seriously \$\endgroup\$ – ATaco Jul 11 '17 at 5:29
  • \$\begingroup\$ 'Shortest answer in bytes for each language wins.' I am not sure about short being the best measure for a pristine program. \$\endgroup\$ – P. Siehr Jul 11 '17 at 7:14
  • \$\begingroup\$ @P.Siehr What would you reccomend instead? \$\endgroup\$ – Shelvacu Jul 11 '17 at 7:16
6
\$\begingroup\$

Haskell, 132 bytes

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Try it online!

This is an extension of the quine

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

which works by concatenating the data string with a quoted version (by using show) of itself and printing the result. However, this is not pristine as any characters in the data string can be removed without failing and also the $(++)<*>show$ or (++)<*> part could be dropped without the program breaking.

To fix this, a custom print function q is defined which checks the length of the given string and calls fail if it's shorter than 132. This catches the removing of any sequence from the data string and also the removals of $(++)<*>show$ or (++)<*>, as in both cases the resulting string passed to q is shorter.

In q the number 132 could be shortened to 1, 13, 32 or 2, but in each case again fail is called.

As far as I can tell, the removal of any other substring causes either an syntax or type error, so the program does not even compile in the first place. (Haskell's strict type system comes in handy here.)

Edit: Thanks to Ørjan Johansen and Shelvacu for pointing out a flaws!

\$\endgroup\$
  • \$\begingroup\$ I'm afraid fail[]|length x/=122 can be removed. fail[]:[putStr x|length x==122] might work better. \$\endgroup\$ – Ørjan Johansen Jul 11 '17 at 15:20
  • \$\begingroup\$ Argh, no, then |length x==122 could be removed. if length x==122 then putStr x else fail[] perhaps? \$\endgroup\$ – Ørjan Johansen Jul 11 '17 at 15:30
  • \$\begingroup\$ @ØrjanJohansen Good catch, I had an if then else before but thought I could shorten it. \$\endgroup\$ – Laikoni Jul 11 '17 at 15:51
  • 2
    \$\begingroup\$ putStr x can become p x, which when I tried on my system ran for a very long time before I killed it, I suspect the tail call recursion was optimized, so it's an infinite loop. I don't know enough haskell to give any suggestions on how to fix it. \$\endgroup\$ – Shelvacu Jul 12 '17 at 8:35
  • \$\begingroup\$ @Shelvacu Whoops. Renaming p to q should fix that. \$\endgroup\$ – Ørjan Johansen Jul 12 '17 at 13:18
4
\$\begingroup\$

Python 3, 113 bytes

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

Try it online!

How it works

We can’t easily use multiple statements since the second one could be deleted, so we start with a single-expression quine:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

To protect it against substring deletions, we use open(1,"w").write instead of print. In Python 3, write returns the number of written characters, which we will verify is 113 to ensure that no part of the string was deleted. We do this by looking up the return value in the dictionary {113:[]}, and looping over the result with for[]in…:a, which will fail if we didn’t get an empty iterable or if the for statement is deleted.

\$\endgroup\$
  • 1
    \$\begingroup\$ Could you give an explanation of how your code works? \$\endgroup\$ – Shelvacu Jul 17 '17 at 22:47
  • \$\begingroup\$ @Shelvacu Yeah, added. \$\endgroup\$ – Anders Kaseorg Jul 17 '17 at 22:59
3
\$\begingroup\$

Standard ML (MLton), 204 182 189 bytes

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".

\$\endgroup\$
  • \$\begingroup\$ −2 bytes by writing "\"" directly and using String.toString for escaping. \$\endgroup\$ – Anders Kaseorg Jan 3 '18 at 12:53
  • \$\begingroup\$ Wait, this is horrible: MLton seems to accept the program ", producing empty output (TIO). \$\endgroup\$ – Anders Kaseorg Jan 3 '18 at 13:17
  • \$\begingroup\$ @AndersKaseorg Huh, that's strange. However it should be possible to fix this issue by using another let ... in ... end. \$\endgroup\$ – Laikoni Jan 3 '18 at 14:33
  • \$\begingroup\$ @AndersKaseorg I fixed the issue, hopefully without introducing new "vulnerabilities". \$\endgroup\$ – Laikoni Jan 4 '18 at 18:42
  • \$\begingroup\$ Actually I looked into MLton accepting " as a program, and it seems that bug was fixed in this commit, so maybe your 182 or my 180 is fine as long as you specify the unreleased Git version of MLton. \$\endgroup\$ – Anders Kaseorg Jan 4 '18 at 19:33
2
\$\begingroup\$

Ruby, 78 bytes

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

I wrote this when I thought of the challenge to make sure it was possible. It uses the same "wrapper" from one of my answers to the original pristine challenge.

Explanation:

  • eval(*[ expr ])

    This evaluates whatever code returns as a ruby program. This effectively tests that the string that code returns is a valid ruby program. Conveniently, ruby programs can be blank, or only consist of whitespace.

    The "splat" operator * allows you to use an array as arguments to a function. This also means that if eval is removed, the resulting program is (*[ expr ]), which is not valid ruby.

  • ($>.write( str )-78).chr

    $> is a short variable for STDOUT.

    $>.write(foo) writes foo to STDOUT and, importantly for this code, returns the number of bytes written.

    $>.write(foo)-78: Here 78 is the length of the program, and so if the program is not mangled, will also be the number of bytes written. Therefor, in the unmangled case, this will return zero.

    num.chr returns num as a character, eg 0.chr will return a string containing a single null byte. In the unmangled program, this will give a string with a single null byte to eval, which is a valid ruby program that is a no-op.

    Also, the program can have a substring removed such that it's just eval(*[(78).chr]) or eval(*[(8).chr]), which means that the numeric constant cannot end with any of the numbers (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) because they are ascii codes for valid single-character ruby programs.

  • %{ str }

    This is a lesser-known syntax for string literals in ruby. The reason it's used here is that balanced pairs of {} can be used within the string, which means this syntax can contain itself. For example, %{foo{bar}} is the same as "foo{bar}".

  • (s=%{ data })%s

    This defines the variable s which is the data of this quine, as a printf string.

    Assignments in ruby return what was assigned, so this is the same as first assigning s and then running s%s

    % on a string is syntatic sugar for ruby's equivalent of sprintf. The %s signifies where within the data the data itself should be embedded.

    This bit of code defines the data portion of the quine and embeds it within itself to create the full code.

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.