rlm@0
|
1 #+title: Calling it quits without parentheses
|
rlm@0
|
2 #+author: Robert McIntyre & Dylan Holmes
|
rlm@0
|
3 #+EMAIL: rlm@mit.edu
|
rlm@0
|
4 #+MATHJAX: align:"left" mathml:t path:"../MathJax/MathJax.js"
|
rlm@0
|
5 #+STYLE: <link rel="stylesheet" type="text/css" href="../css/aurellem.css" />
|
rlm@0
|
6 #+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
|
rlm@1
|
7 #+SETUPFILE: ../../aurellem/org/level-0.org
|
rlm@1
|
8 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@1
|
9 #+BABEL: :mkdirp yes
|
rlm@0
|
10
|
rlm@0
|
11 [TABLE-OF-CONTENTS]
|
rlm@0
|
12
|
rlm@0
|
13 # * Calling it quits without parentheses
|
rlm@0
|
14 Is it possible to make a =quit= command for Clojure such that simply
|
rlm@0
|
15 typing the word =quit= at an REPL exits the REPL? An intuitive =quit=
|
rlm@0
|
16 command would be especially useful for people new to Lisp syntax or
|
rlm@0
|
17 the REPL; the challenge is to make a genuine Clojure command that
|
rlm@0
|
18 executes without being called with parentheses. We found three
|
rlm@0
|
19 solutions.
|
rlm@0
|
20
|
rlm@0
|
21 #+srcname: header
|
rlm@0
|
22 #+begin_src clojure :results silent
|
rlm@0
|
23 (ns abomination.no-parens
|
rlm@0
|
24 (:use clojure.contrib.def))
|
rlm@0
|
25 #+end_src
|
rlm@0
|
26
|
rlm@0
|
27 * By modifying =toString=
|
rlm@0
|
28 #+srcname: toString
|
rlm@0
|
29 #+begin_src clojure
|
rlm@0
|
30 (in-ns 'abomination.no-parens)
|
rlm@0
|
31 (gen-class :name abomination.no-parens.Quit
|
rlm@0
|
32 :prefix quit-)
|
rlm@0
|
33
|
rlm@0
|
34 (defn quit-toString
|
rlm@0
|
35 [this]
|
rlm@0
|
36 (System/exit 0))
|
rlm@0
|
37
|
rlm@1
|
38
|
rlm@0
|
39 (defvar quit (abomination.no-parens.Quit.)
|
rlm@0
|
40 "a sneaky way to support a `quit` command")
|
rlm@0
|
41 #+end_src
|
rlm@0
|
42
|
rlm@1
|
43 When you type any variable at the REPL, the REPL attempts to print it
|
rlm@1
|
44 as a nicely-formatted string by calling its =toString= method. Our
|
rlm@1
|
45 trick is to define a class with a =toString= method that exits the
|
rlm@1
|
46 REPL; this trick ensures that any variable of that class will close
|
rlm@0
|
47 the REPL when the REPL attempts to print it.
|
rlm@0
|
48
|
rlm@1
|
49 First, we use =gen-class= to make a new class named Quit; in that same
|
rlm@1
|
50 line, we use =:prefix= to establish the convention that any function
|
rlm@1
|
51 named =quit-[something]= will be adopted as the =[something]= method
|
rlm@1
|
52 for the newly-defined Quit class. We use this convention to write our
|
rlm@1
|
53 own =toString= method for Quit.
|
rlm@0
|
54
|
rlm@1
|
55 Next, we define a suitable =toString= method for the Quit class so
|
rlm@1
|
56 that attempting to print an instance of the Quit class has the effect
|
rlm@1
|
57 of closing the REPL. We do this by defining a function =quit-toString=
|
rlm@0
|
58 which closes the REPL; by the convention established above, the Quit
|
rlm@1
|
59 class automatically adopts =quit-toString= as its =toString= method.
|
rlm@0
|
60
|
rlm@0
|
61 Finally, we use =defvar= to create an instance of the Quit class; we
|
rlm@0
|
62 name this instance =quit=. Now when you type =quit= into the REPL, the
|
rlm@1
|
63 REPL executes the =toString= method of the Quit class, exiting the
|
rlm@1
|
64 REPL instead of returning a string.
|
rlm@0
|
65
|
rlm@0
|
66 #+begin_src clojure :exports both
|
rlm@1
|
67 (binding [*compile-path* "/home/r/proj/abomination/classes"]
|
rlm@0
|
68 (compile 'abomination.no-parens))
|
rlm@1
|
69
|
rlm@0
|
70 #+end_src
|
rlm@0
|
71
|
rlm@0
|
72 #+results:
|
rlm@0
|
73 : abomination.no-parens
|
rlm@0
|
74
|
rlm@0
|
75 * By wrapping the command in a lazy sequence
|
rlm@0
|
76 #+srcname: lazy-seq
|
rlm@0
|
77 #+begin_src clojure :results silent
|
rlm@0
|
78 (in-ns 'abomination.no-parens)
|
rlm@0
|
79 (defvar quit* (lazy-seq :the-great-bringer-of-death! (System/exit 0))
|
rlm@0
|
80 "the first time it's evaulated it will kill the JVM")
|
rlm@0
|
81 #+end_src
|
rlm@0
|
82
|
rlm@0
|
83 * By =delay=-ing the command
|
rlm@0
|
84 #+srcname: delay
|
rlm@0
|
85 #+begin_src clojure :results silent
|
rlm@0
|
86 (in-ns 'abomination.no-parens)
|
rlm@0
|
87 (defvar quit** (delay (System/exit 0))
|
rlm@0
|
88 "when this is evaulated at the REPL, it will exit the JVM.")
|
rlm@0
|
89 #+end_src
|
rlm@0
|
90
|
rlm@0
|
91 The same thing, accomplished in a much more elegant and clojureish
|
rlm@0
|
92 way.
|
rlm@0
|
93
|
rlm@0
|
94 # STUD CRUFT PIZZA
|
rlm@0
|
95
|
rlm@0
|
96 #+begin_quote
|
rlm@0
|
97 And death i think is no parenthesis
|
rlm@0
|
98 \mdash{}E. E. Cummings
|
rlm@0
|
99 #+end_quote
|
rlm@1
|
100
|
rlm@1
|
101
|
rlm@1
|
102 * COMMENT code generation
|
rlm@1
|
103 #+begin_src clojure :tangle ../src/abomination/no_parens.clj :results silent :exports none :noweb yes
|
rlm@1
|
104 <<header>>
|
rlm@1
|
105 <<toString>>
|
rlm@1
|
106 <<lazy-seq>>
|
rlm@1
|
107 <<delay>>
|
rlm@1
|
108 #+end_src
|