rlm@0: #+title: Calling it quits without parentheses
rlm@0: #+author: Robert McIntyre & Dylan Holmes
rlm@0: #+EMAIL: rlm@mit.edu
rlm@0: #+MATHJAX: align:"left" mathml:t path:"../MathJax/MathJax.js"
rlm@0: #+STYLE:
rlm@0: #+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
rlm@0: #+SETUPFILE: ../templates/level-0.org
rlm@0: #+INCLUDE: ../templates/level-0.org
rlm@0:
rlm@0: [TABLE-OF-CONTENTS]
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@0: (defvar quit (abomination.no-parens.Quit.)
rlm@0: "a sneaky way to support a `quit` command")
rlm@0: #+end_src
rlm@0:
rlm@0: When you type any variable at the REPL, the REPL attempts to print it as a nicely-formatted string by calling its =toString=
rlm@0: method. Our trick is to define a class with a =toString= method
rlm@0: that exits the 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@0: First, we use =gen-class= to make a new class named Quit; in that same line, we use
rlm@0: =:prefix= to establish the convention that any function named
rlm@0: =quit-[something]= will be adopted as the =[something]= method for the
rlm@0: newly-defined Quit class. We use this convention to write our own
rlm@0: =toString= method for Quit.
rlm@0:
rlm@0: Next, we define a suitable =toString= method for the Quit class so that
rlm@0: attempting to print an instance of the Quit class has the effect of
rlm@0: 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@0: 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@0: REPL executes the =toString= method of the Quit class, exiting the REPL instead of returning a
rlm@0: string.
rlm@0:
rlm@0: #+begin_src clojure :exports both
rlm@0: (binding [*compile-path* "/home/r/aurellem/classes"]
rlm@0: (compile 'abomination.no-parens))
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: #+begin_src clojure :tangle no_parens.clj :results silent :exports none :noweb yes
rlm@0: <>
rlm@0: <>
rlm@0: <>
rlm@0: <>
rlm@0: #+end_src
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