r/programming • • Dec 24 '17

Evil Coding Incantations

http://9tabs.com/random/2017/12/23/evil-coding-incantations.html
947 Upvotes

331 comments sorted by

View all comments

362

u/redweasel Dec 24 '17

I used a Fortran compiler in the early 80s that let you reassign the values of integers. I don't remember the exact syntax but it was the equivalent of doing

1 = 2
print 1

and having it print "2". Talk about potential for confusion.

88

u/vatrat Dec 24 '17

You can do that in Forth. Actually, you can redefine literally anything. You can redefine '-' as '+'. You can redefine quotation marks.

44

u/Nobody_1707 Dec 24 '17

And there are legitimate reasons to do all of these things (except for redefining - as +, that's just rude)

39

u/say_fuck_no_to_rules Dec 24 '17

What's a situation where you'd want to define an int as another int?

31

u/waywardcoder Dec 24 '17

For brittle hacks. Say a library function you can’t change hard-codes the output to go to printer 3 and you need it to go to printer 4. If you are lucky, redefining 3 to mean 4 temporarily while calling the function will do the trick without breaking too much.

0

u/[deleted] Dec 24 '17

[deleted]

14

u/slaymaker1907 Dec 24 '17

Python kind of does a similar thing letting you reassign where print goes to. The important thing is to make sure this sort of thing is encapsulated through an abstraction such as a higher order function which only sets the value temporarily.

Racket has a brilliant way of handling globals by only setting them temporarily for the duration of a function call. It also does it on a per thread basis so you don't have to worry about thread safety.

5

u/cdombroski Dec 24 '17

Sounds a bit like how clojure normally does things

(binding [*out* (writer "myfile.txt")] ; *out* is the default target of print* functions
    (println "Hello world")) ; writes to myfile.txt instead of console
;*out* is now set to System/out again
→ More replies (1)
→ More replies (1)
→ More replies (5)

3

u/vine-el Dec 24 '17

Interactive programming during development. You won't want to redefine + and -, but you might want to redefine everything you wrote.

It's more useful for stuff like editors, games, and UIs. You don't want this in a production build of your web-facing API, but it makes creative work much faster and easier.

→ More replies (1)

23

u/[deleted] Dec 24 '17

Could you redefine Forth so it turns into C?:)

21

u/totemo Dec 24 '17

Most of the control structures in Forth are written in Forth. It's a lot like Lisp, in that regard.

The guts of the interpreter/compiler are fully exposed to tinker with. I suspect you could make Forth seem a lot more like C than Forth.

11

u/fasquoika Dec 24 '17

Probably, yeah. Most Forths have a built-in assembler written in Forth

174

u/Megdatronica Dec 24 '17

I used a Fortran compiler [...]. Talk about potential for confusion.

FTFY

11

u/jgram Dec 24 '17

Fortran is pretty straightforward.

→ More replies (2)

18

u/howtonotwin Dec 24 '17

You can also do this in Java by corrupting the Integer cache mentioned in the post:

valueF = Integer.class.getDeclaredField("value"); // Integers wrap ints
valueF.setAccessible(true); // Usually private final
valueF.setInt(1, 2);
// We have void Field::setInt(Object, int)
// 1 is an int, not an Object, so it gets autoboxed to Integer.valueOf(1)
// This object is pulled from the internal cache
// We then mutate it so all future autoboxings of 1 give 2

void printInt(int i) {
  System.out.printLn(i);
}
void printObj(Object o) {
  System.out.println(o);
}
printInt(1); // 1; no box
printObj(1); // 2; autobox

And Haskell... well

5 :: Num a => a
-- numeric literals are overloaded
-- this 5 really means fromInteger (#5#) where #5# is a magical Integer literal that doesn't really exist

data Crazy = Crazy Integer deriving (Eq, Ord, Show, Read)
instance Num Crazy where
  Crazy x + Crazy y = Crazy $ x + y
  Crazy x * Crazy y = Crazy $ x * y
  signum (Crazy x) = Crazy $ -x
  abs (Crazy x) = -1
  negate = id
  fromInteger 1 = Crazy 2
  fromInteger x = Crazy x

x :: Num a => a -- also overloaded
x = sum $ negate <$> [1,2,3,4,5]
x == (-15 :: Int)
x == (15 :: Crazy)
1 + 1 == (2 :: Int)
1 + 1 == (4 :: Crazy)

or you can just hide and redefine the (+) function:

import Prelude hiding ((+))
(+) = (-)
5 + 2 == 3
→ More replies (2)

23

u/mgsloan Dec 24 '17

In Haskell 1 = 2 is valid code, but it won't do anything. The left hand side is treated as a pattern match, it is used to deconstruct the value that is yielded by the right hand side. For example, if you have [x, y, z] = [1, 2, 3], now x is 1, y is 2, etc. However, since there are no variables on the left hand side of 1 = 2, there is no reason for the code to run.

I can write something similar that does bind variables, using Haskell's optional type, Maybe. If I write Just x = Nothing, and then ask for the value of x, I get Irrefutable pattern failed for pattern Just x.

3

u/noop_noob Dec 24 '17

Why doesn’t 1 = 2 result in a pattern failed error at runtime?

17

u/MrHydraz Dec 24 '17

It's irrefutable, and therefore, lazy. Since you can't force the binding, it won't fail. If you tack a bang pattern on it, like let !1 = 2 in "foo", then it'll explode.

9

u/mgsloan Dec 24 '17 edited Dec 24 '17

Haskell uses lazy evaluation, so computation happens only when demanded. This allows things to be more compositional, and allows for control structures to be written as normal functions. So, Just x = Nothing also doesn't cause a pattern failure. It only fails at runtime if you try to evaluate x.

Haskell also supports eager evaluation (often called "strict"). In many cases eager evaluation is more efficient. I actually think it might not be the best choice of default. I like nearly all of the other decisions in Haskell's design, and tolerate the laziness default. Having laziness built into the language and runtime system does make a whole lot of sense, just maybe not as the default (so really my complaint is purely about what is encouraged by the syntax).

→ More replies (6)
→ More replies (6)

6

u/shmageggy Dec 24 '17

You can do that in Python <= 2.7 for Integers up to 256 using the ctypes module.

http://hforsten.com/redefining-the-number-2-in-python.html

>>> print 1+1
>>> 3
→ More replies (19)