46
\$\begingroup\$

Recently I had the pleasure of writing a Haskell program that could detect if the NegativeLiterals extension was engaged. I came up with the following:

data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$1==u(-1)

Try it online!

This will print True normally and False otherwise.

Now I had so much fun doing this I'm extending the challenge to all of you. What other Haskell language extensions can you crack?

Rules

To crack a particular language extension you must write a Haskell program that compiles both with and without the language extension (warnings are fine) and outputs two different non-error values when run with the language extension and it turned off (by adding the No prefix to the language extension). In this way the code above could be shortened to just:

data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$u(-1)

which prints 1 and -1.

Any method you use to crack a extension must be specific to that extension. There may be ways of arbitrarily detecting what compiler flags or LanguageExtensions are enabled, if so such methods are not allowed. You may enable additional language extensions or change the compiler optimization using -O at no cost to your byte count.

Language extensions

You cannot crack any language extension that does not have a No counterpart (e.g. Haskell98, Haskell2010, Unsafe, Trustworthy, Safe) because these do not fall under the terms outlined above. Every other language extension is fair game.

Scoring

You will be awarded one point for every language extensions you are the first person to crack and one additional point for every language extension for which you have the shortest (measured in bytes) crack. For the second point ties will be broken in favor of earlier submissions. Higher score is better

You will not be able to score a point for first submission on NegativeLiterals or QuasiQuotes because I have already cracked them and included them in the body of the post. You will however be able to score a point for the shortest crack of each of these. Here is my crack of QuasiQuotes

import Text.Heredoc
main=print[here|here<-""] -- |]

Try it online!

\$\endgroup\$
27
  • \$\begingroup\$ Can programs take input? \$\endgroup\$
    – H.PWiz
    Jan 21, 2018 at 19:19
  • 3
    \$\begingroup\$ I think this is a list of all valid options \$\endgroup\$
    – H.PWiz
    Jan 21, 2018 at 20:32
  • 1
    \$\begingroup\$ Note that my above comment does not include NondecreasingIndentation for obvious reasons \$\endgroup\$
    – H.PWiz
    Jan 21, 2018 at 21:12
  • 1
    \$\begingroup\$ I'm quite curious whether it's possible to crack RelaxedPolyRec, for a compiler ancient enough to actually support turning it off. (The option hung around, with documentation, for some years after it stopped doing anything.) \$\endgroup\$
    – dfeuer
    Mar 7, 2019 at 8:51
  • 1
    \$\begingroup\$ @dfeuer Looking at this ticket it seems like GHC 6.12.1 supported turning it off. \$\endgroup\$ Mar 8, 2019 at 6:35

34 Answers 34

1
2
2
\$\begingroup\$

OverloadedStrings, 77 bytes

import Data.String
instance IsString Double where fromString _=0
main=print""

Try it online!

Other OverloadedStrings answers: by ბიმო, by felixphew. This one does not require other extensions. Created by Ørjan Johansen.

\$\endgroup\$
1
\$\begingroup\$

NegativeLiterals, 20 bytes

main=print$ -1`mod`2

Try it online!

Copied from this answer.

\$\endgroup\$
1
\$\begingroup\$

LexicalNegation, 20 bytes

main=print$ -1`mod`2

(No TIO: ghc 8.2.2 < 9.0.1)

Same as NegativeLiterals.

\$\endgroup\$
1
\$\begingroup\$

MonomorphismRestriction, 24 bytes

p=1
x=p+0.0
main=print$p

-XMonomorphismRestriction gives 1.0

-XNoMonomorphismRestriction gives 1

Explanation

So here we have p which I assign to 1. Now in Haskell 1 is a numeric literal and can be any Num type. Next we assign x to p+0.0, 0.0 is another literal, but it's a fractional literal so it has a more specific type than 1. That's ok since Fractionals are also Nums. So x is just some fractional type. Then we print p.

So how does the monomorphism restriction come into play? Well since p doesn't have a type anotation, the compiler needs to come up with a type for p. With the monomorphism restriction this type is going to be concrete, i.e. it's going to come up with the first type it can rather than the most general kind. It sees that p is used as a fractional in the definition of x so it picks Float as the type for p. When we print p it's then printing a float, so it prints a decimal.

Without the monomorphism restriction the compiler takes some extra time and figures out that there is a polymorphic type that can be given to p. So it assigns it the polymorphic type Num a => a rather than Float. It then doesn't matter what's going on in x as long as it type checks it can't influence the type of p. When we print p at the end then the compiler once again has to guess a concrete type, however since the type is fully polymorphic it can choose its preferred default of Int and thus it prints 1 as an Int without the decimal place.

So here we are using type defaulting to control the type of the object that gets printed. Since 1 prints differently as a Float and an Int this results in different output.

\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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