Finish step8
[jackhill/mal.git] / docs / FAQ.md
1 # Mal/Make-a-Lisp FAQ
2
3 <a name="why_mal"></a>
4
5 ### Why did you create mal/make-a-lisp?
6 ### OR Why the name "mal"?
7 ### OR Why?
8 ### OR Wat?
9
10 In November of 2013, Alan Dipert gave a [lightning talk at
11 Clojure/conj](https://www.youtube.com/watch?v=bmHTFo2Rf2w#t=28m55s)
12 about [gherkin](https://github.com/alandipert/gherkin), a Lisp
13 implemented in bash. His presentation led me to ask myself the question
14 of whether a Lisp could be created using the GNU Make macro language.
15 As you have probably guessed, the answer to that question is yes.
16
17 Interestingly, the current pedagogical/educational purpose of mal
18 happened due to a semantic naming accident (naming is such a fraught
19 task in computer science). If I am remembering correctly, the name
20 "mal" original meant "MAke Lisp". I do not remember precisely why
21 I continued to create more implementations, apart from the fact that
22 it was a fun challenge, but after the make implementation, many of the
23 others were relatively easy. At some point during that process,
24 I realized that the multiple implementations and incremental steps
25 (which was originally just for my own clarity) was a useful learning
26 tool and so the "mal" name became a double entendre for "Make, A Lisp"
27 and "make-a-lisp" (and eventually just the latter given that the make
28 implementation is now just a small part of the whole).
29
30
31 <a name="code_split"></a>
32
33 ### Why is some code split into steps and some code not?
34
35 The split between code that goes in steps and code that goes into other files
36 is not completely arbitrary (a bit arbitrary, but not completely). My rule of
37 thumb is something like this: if the code is specific and necessary for
38 implementing a Lisp then it belongs in the step files. If the purpose of the
39 code is for implementing new dynamic data-types/objects and the functions or
40 methods that operate on those types, then it goes in separate files.
41
42 If the target language has types and functions that resemble mal types, then
43 those files tend to be very small or non-existent. Examples:
44
45 * the mal implementation has no types, reader, printer files and
46 has a trivial core file (just to hoist underlying functions)
47 * the Clojure implementation has no types file and a fairly trivial
48 core file
49 * ruby types and the functions that operate on them are very "Lispy"
50 so the Ruby types file and core file are very small.
51
52 The env file is somewhat more arbitrary, however, it is
53 a self-contained module that is implemented early and changes very
54 little after that, so I decided to separate it. Also, for languages
55 that have hierarchical maps/dictionaries (e.g. Javascript
56 objects/prototype chain), you do not necessarily need an env file.
57
58 Another way of summarizing this answer is that the step files
59 represent the core of what makes something a Lisp, the rest of the
60 modules are just language specific details (they may be the harder
61 than the Lisp part, but that is due to the nature of the target
62 language not because of Lisp functionality per se).
63
64
65 <a name="steps"></a>
66
67 ### Why are the mal/make-a-lisp steps structured the way they are?
68
69 ### OR Why is X functionality in step Y instead of step Z?
70
71 There is no single consistent rule that I have used to determine which
72 functionality goes in which step and the arrangement has changed
73 numerous times since the beginning of the project. There are several
74 different goals that I try and balance in determining which
75 functionality goes into which step:
76
77 * **Optimize Lisp learning**: I want developers who are unfamiliar with
78 Lisp to be able to use the project and guide to learn about Lisp
79 without becoming overwhelmed. In many Lisp introductions, concepts
80 like quoting and homoiconicity (i.e. a user exposed eval function)
81 are introduced early. But these are fairly foreign to most other
82 languages so they are introduced in later steps in mal. I also try
83 to not to concentrate too many Lisp concepts in a single step. So
84 many steps contain one or two Lisp concepts plus some core function
85 additions that support those concepts.
86
87 * **Optimize implementation language learning (equal-ish step
88 sizing)**: I try to structure the steps so that the target
89 implementation can be learned incrementally. This goal is the one
90 that has caused me to refactor the steps the most. Different
91 languages have different areas that they optimize and make simple
92 for the developer. For example, in Java (prior to 8) and PostScript
93 creating the equivalent of anonymous functions and function closures
94 is painful. In other languages, function closures are trivial, but
95 IO and error handling are tedious when you are first learning the
96 language (I am looking at you Haskell). So this goal is really about
97 trying to balance step size across multiple languages.
98
99 * **Practical results early and continuous feedback**: it is
100 a scientific fact that many small rewards are more motivating than
101 a single large reward (citation intentionally omitted, get a small
102 reward by googling it yourself). Each step in mal adds new
103 functionality that can actually be exercised by the implementer and,
104 just as importantly, easily tested.
105
106 Also, the step structure of mal/make-a-lisp is not perfect. It never
107 will be perfect, but there are some areas that could be improved. The
108 most glaring problem is that step1 is on the heavy/large size because
109 in most languages you have to implement a good portion of the
110 reader/printer before you can begin using/testing the step. The
111 compromise I have settled on for now is to put extra detail in the
112 process guide for step1 and to be clear that many of the types are
113 deferrable until later. But I am always open to suggestions.
114
115
116 <a name="add_implementation"></a>
117
118 ### Will you add my new implementation?
119
120 Absolutely! I want mal to have a idiomatic implementation in every
121 programming language.
122
123 Here are a few guidelines for getting your implementation accepted
124 into the main repository:
125
126 * Your implementation should follow the existing mal steps and
127 structure: Lisp-centric code (eval, eval_ast, quasiquote,
128 macroexpand) in the step files, other code in reader, printer, env,
129 and core files. See [code layout rationale](#code_split) above.
130 I encourage you to create implementations that take mal in new
131 directions for your own learning and experimentation, but for it to
132 be included in the main repository I ask that it follows the steps
133 and structure.
134
135 * Your implementation should stick as much as possible to the accepted
136 idioms and conventions in that language. Try to create an
137 implementation that will not make an expert in that language say
138 "Woah, that's a strange way of doing things". And on that topic,
139 I make no guarantees that the existing implementations are
140 particularly idiomatic in their target languages (improvements are
141 welcome). However, if it is clear to me that your implementation is
142 not idiomatic in a given language then I will probably ask you to
143 improve it first.
144
145 * Your implementation needs to be complete enough to self-host. This
146 means that all the mandatory tests should pass in both direct and
147 self-hosted modes:
148 ```bash
149 make "test^[IMPL_NAME]"
150 make MAL_IMPL=[IMPL_NAME] "test^mal"
151 ```
152 You do not need to pass the final optional tests for stepA that are
153 marked as optional and not needed for self-hosting (except for the
154 `time-ms` function which is needed to run the micro-benchmark tests).
155
156 * Create a `Dockerfile` in your directory that installs all the
157 packages necessary to build and run your implementation. Refer to other
158 implementations for examples of what the Dockerfile should contain.
159 Build your docker image and tag it `kanaka/mal-test-[IMPL_NAME]`.
160 The top-level Makefile has support for building/testing within
161 docker with the `DOCKERIZE` flag:
162 ```bash
163 make DOCKERIZE=1 "test^[IMPL_NAME]"
164 make DOCKERIZE=1 MAL_IMPL=[IMPL_NAME] "test^mal"
165 ```
166
167 * Make sure the Travis build and test scripts pass locally:
168 ```bash
169 IMPL=[IMPL_NAME] ./.travis_build.sh
170 ./.travis_test.sh test [IMPL_NAME]
171 ```
172
173 * If you are creating a new implementation for an existing
174 implementation (or somebody beats you to the punch while you are
175 working on it), there is still a chance I will merge your
176 implementation. If you can make a compelling argument that your
177 implementation is more idiomatic or significantly better in some way
178 than the existing implementation then I may replace the existing
179 one. However, if your approach is different or unique from the
180 existing implementation, there is still a good chance I will merge
181 your implementation side-by-side with the existing one. At the very
182 least, even if I decide not to merge your implementation, I am
183 certainly willing to link to you implementation once it is
184 completed.
185
186 * You do not need to implement line editing (i.e. readline)
187 functionality for your implementation, however, it is a nice
188 convenience for users of your implementation and I personally find
189 it saves a lot of time when I am creating a new implementation to
190 have line edit support early in the process.
191
192 ### Why do some mal forms end in "\*" or "!" (swap!, def!, let\*, etc)?
193
194 The forms that end in a bang mutate something:
195 * **def!** mutates the current environment
196 * **swap!** and **reset!** mutate an atom to refer to a new value
197
198 The forms that end in a star are similar to similar Clojure forms but
199 are more limited in functionality:
200 * **fn\*** does not do parameter destructuring and only supports
201 a single body form.
202 * **let\*** does not do parameter destructuring
203 * **try\*** and **catch\*** do not support type matching of
204 exceptions
205