rlm@0: #+title: Calling it quits without parentheses rlm@0: #+author: Robert McIntyre & Dylan Holmes rlm@0: #+EMAIL: rlm@mit.edu rlm@3: #+SETUPFILE: ../../aurellem/org/setup.org rlm@3: #+INCLUDE: ../../aurellem/org/level-0.org rlm@3: rlm@0: rlm@0: # * Calling it quits without parentheses rlm@0: Is it possible to make a =quit= command for Clojure such that simply rlm@0: typing the word =quit= at an REPL exits the REPL? An intuitive =quit= rlm@0: command would be especially useful for people new to Lisp syntax or rlm@0: the REPL; the challenge is to make a genuine Clojure command that rlm@0: executes without being called with parentheses. We found three rlm@0: solutions. rlm@0: rlm@0: #+srcname: header rlm@0: #+begin_src clojure :results silent rlm@0: (ns abomination.no-parens rlm@0: (:use clojure.contrib.def)) rlm@0: #+end_src rlm@0: rlm@0: * By modifying =toString= rlm@0: #+srcname: toString rlm@0: #+begin_src clojure rlm@0: (in-ns 'abomination.no-parens) rlm@0: (gen-class :name abomination.no-parens.Quit rlm@0: :prefix quit-) rlm@0: rlm@0: (defn quit-toString rlm@0: [this] rlm@0: (System/exit 0)) rlm@0: rlm@1: rlm@0: (defvar quit (abomination.no-parens.Quit.) rlm@0: "a sneaky way to support a `quit` command") rlm@0: #+end_src rlm@0: rlm@1: When you type any variable at the REPL, the REPL attempts to print it rlm@1: as a nicely-formatted string by calling its =toString= method. Our rlm@1: trick is to define a class with a =toString= method that exits the rlm@1: REPL; this trick ensures that any variable of that class will close rlm@0: the REPL when the REPL attempts to print it. rlm@0: rlm@1: First, we use =gen-class= to make a new class named Quit; in that same rlm@1: line, we use =:prefix= to establish the convention that any function rlm@1: named =quit-[something]= will be adopted as the =[something]= method rlm@1: for the newly-defined Quit class. We use this convention to write our rlm@1: own =toString= method for Quit. rlm@0: rlm@1: Next, we define a suitable =toString= method for the Quit class so rlm@1: that attempting to print an instance of the Quit class has the effect rlm@1: of closing the REPL. We do this by defining a function =quit-toString= rlm@0: which closes the REPL; by the convention established above, the Quit rlm@1: class automatically adopts =quit-toString= as its =toString= method. rlm@0: rlm@0: Finally, we use =defvar= to create an instance of the Quit class; we rlm@0: name this instance =quit=. Now when you type =quit= into the REPL, the rlm@1: REPL executes the =toString= method of the Quit class, exiting the rlm@1: REPL instead of returning a string. rlm@0: rlm@0: #+begin_src clojure :exports both rlm@1: (binding [*compile-path* "/home/r/proj/abomination/classes"] rlm@0: (compile 'abomination.no-parens)) rlm@1: rlm@0: #+end_src rlm@0: rlm@0: #+results: rlm@0: : abomination.no-parens rlm@0: rlm@0: * By wrapping the command in a lazy sequence rlm@0: #+srcname: lazy-seq rlm@0: #+begin_src clojure :results silent rlm@0: (in-ns 'abomination.no-parens) rlm@0: (defvar quit* (lazy-seq :the-great-bringer-of-death! (System/exit 0)) rlm@0: "the first time it's evaulated it will kill the JVM") rlm@0: #+end_src rlm@0: rlm@0: * By =delay=-ing the command rlm@0: #+srcname: delay rlm@0: #+begin_src clojure :results silent rlm@0: (in-ns 'abomination.no-parens) rlm@0: (defvar quit** (delay (System/exit 0)) rlm@0: "when this is evaulated at the REPL, it will exit the JVM.") rlm@0: #+end_src rlm@0: rlm@0: The same thing, accomplished in a much more elegant and clojureish rlm@0: way. rlm@0: rlm@0: # STUD CRUFT PIZZA rlm@0: rlm@0: #+begin_quote rlm@0: And death i think is no parenthesis rlm@0: \mdash{}E. E. Cummings rlm@0: #+end_quote rlm@1: rlm@1: rlm@1: * COMMENT code generation rlm@1: #+begin_src clojure :tangle ../src/abomination/no_parens.clj :results silent :exports none :noweb yes rlm@1: <
> rlm@1: <> rlm@1: <> rlm@1: <> rlm@1: #+end_src