Commit | Line | Data |
---|---|---|
3cbd2ef7 | 1 | # Mal/Make-a-Lisp FAQ |
263007a8 | 2 | |
8d8ca1f0 JM |
3 | <a name="why_mal"></a> |
4 | ||
81f5db50 JM |
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 | |
e1ab693c | 13 | implemented in bash. His presentation led me to ask myself the question |
1e66ee3f JM |
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. | |
81f5db50 JM |
16 | |
17 | Interestingly, the current pedagogical/educational purpose of mal | |
18 | happened due to a semantic naming accident (naming is such a fraught | |
1e66ee3f JM |
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). | |
81f5db50 JM |
29 | |
30 | ||
8d8ca1f0 JM |
31 | <a name="code_split"></a> |
32 | ||
263007a8 JM |
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 fairly trivial | |
48 | reader and printer files (just to modify the Clojure reader/writer | |
49 | slightly) and a fairly trivial core file | |
50 | * ruby types and the functions that operate on them are very "Lispy" | |
51 | so the Ruby types file and core file are very small. | |
52 | ||
53 | The env file is somewhat more arbitrary, however, it is | |
54 | a self-contained module that is implemented early and changes very | |
55 | little after that, so I decided to separate it. Also, for languages | |
56 | that have hierarchical maps/dictionaries (e.g. Javascript | |
57 | objects/prototype chain), you do not necessarily need an env file. | |
58 | ||
59 | Another way of summarizing this answer is that the step files | |
60 | represent the core of what makes something a Lisp, the rest of the | |
61 | modules are just language specific details (they may be the harder | |
62 | than the Lisp part, but that is due to the nature of the target | |
63 | language not because of Lisp functionality per se). | |
64 | ||
8d8ca1f0 JM |
65 | |
66 | <a name="steps"></a> | |
67 | ||
263007a8 JM |
68 | ### Why are the mal/make-a-lisp steps structured the way they are? |
69 | ||
70 | ### OR Why is X functionality in step Y instead of step Z? | |
71 | ||
72 | There is no single consistent rule that I have used to determine which | |
73 | functionality goes in which step and the arrangement has changed | |
74 | numerous times since the beginning of the project. There are several | |
75 | different goals that I try and balance in determining which | |
76 | functionality goes into which step: | |
77 | ||
78 | * **Optimize Lisp learning**: I want developers who are unfamiliar with | |
79 | Lisp to be able to use the project and guide to learn about Lisp | |
80 | without becoming overwhelmed. In many Lisp introductions, concepts | |
81 | like quoting and homoiconicity (i.e. a user exposed eval function) | |
82 | are introduced early. But these are fairly foreign to most other | |
83 | languages so they are introduced in later steps in mal. I also try | |
84 | to not to concentrate too many Lisp concepts in a single step. So | |
85 | many steps contain one or two Lisp concepts plus some core function | |
86 | additions that support those concepts. | |
87 | ||
88 | * **Optimize implementation language learning (equal-ish step | |
89 | sizing)**: I try to structure the steps so that the target | |
90 | implementation can be learned incrementally. This goal is the one | |
91 | that has caused me to refactor the steps the most. Different | |
92 | languages have different areas that they optimize and make simple | |
93 | for the developer. For example, in Java (prior to 8) and PostScript | |
94 | creating the equivalent of anonymous functions and function closures | |
95 | is painful. In other languages, function closures are trivial, but | |
96 | IO and error handling are tedious when you are first learning the | |
97 | language (I am looking at you Haskell). So this goal is really about | |
98 | trying to balance step size across multiple languages. | |
99 | ||
100 | * **Practical results early and continuous feedback**: it is | |
101 | a scientific fact that many small rewards are more motivating than | |
102 | a single large reward (citation intentionally omitted, get a small | |
103 | reward by googling it yourself). Each step in mal adds new | |
104 | functionality that can actually be exercised by the implementor and, | |
105 | just as importantly, easily tested. | |
106 | ||
107 | Also, the step structure of mal/make-a-lisp is not perfect. It never | |
108 | will be perfect, but there are some areas that could be improved. The | |
109 | most glaring problem is that step1 is on the heavy/large size because | |
110 | in most languages you have to implement a good portion of the | |
111 | reader/printer before you can begin using/testing the step. The | |
112 | compromise I have settled on for now is to put extra detail in the | |
113 | process guide for step1 and to be clear that many of the types are | |
114 | deferrable until later. But I am always open to suggestions. | |
81f5db50 JM |
115 | |
116 | ||
8d8ca1f0 JM |
117 | <a name="add_implementation"></a> |
118 | ||
81f5db50 JM |
119 | ### Will you add my new implementation? |
120 | ||
121 | Absolutely! I want mal to have a idiomatic implementation in every | |
122 | programming language. | |
123 | ||
124 | Here are a few guidelines for getting your implementation accepted | |
125 | into the main repository: | |
126 | ||
127 | * Your implementation needs to be complete enough to self-host. This | |
280688b2 JM |
128 | means that all the mandatory tests should pass in both direct and |
129 | self-hosted modes: | |
81f5db50 | 130 | ```bash |
e5737b08 SL |
131 | make "test^[IMPL_NAME]" |
132 | make MAL_IMPL=[IMPL_NAME] "test^mal" | |
81f5db50 JM |
133 | ``` |
134 | You do not need to pass the final optional tests for stepA that are | |
135 | marked as optional and not needed for self-hosting. | |
136 | ||
137 | * Your implementation should follow the existing mal steps and | |
8d8ca1f0 JM |
138 | structure: Lisp-centric code (eval, eval_ast, quasiquote, |
139 | macroexpand) in the step files, other code in reader, printer, env, | |
140 | and core files. See [code layout rationale](#code_split) above. | |
141 | I encourage you to create implementations that take mal in new | |
142 | directions for your own learning and experimentation, but for it to | |
143 | be included in the main repository I ask that it follows the steps | |
144 | and structure. | |
81f5db50 JM |
145 | |
146 | * Your implementation should stick as much as possible to the accepted | |
147 | idioms and conventions in that language. Try to create an | |
148 | implementation that will not make an expert in that language say | |
149 | "Woah, that's a strange way of doing things". And on that topic, | |
150 | I make no guarantees that the existing implementations are | |
151 | particularly idiomatic in their target languages (improvements are | |
152 | welcome). However, if it is clear to me that your implementation is | |
153 | not idiomatic in a given language then I will probably ask you to | |
154 | improve it first. | |
155 | ||
156 | * If you are creating a new implementation for an existing | |
068b8d35 JM |
157 | implementation (or somebody beats you to the punch while you are |
158 | working on it), there is still a chance I will merge your | |
159 | implementation. If you can make a compelling argument that your | |
160 | implementation is more idiomatic or significantly better than the | |
161 | existing implementation then I may replace the existing one. | |
162 | However, if your approach is different or unique from the existing | |
163 | implementation, there is still a good chance I will merge your | |
164 | implementation side-by-side with the existing one. In that case | |
165 | I will add your github username as a suffix to the language | |
166 | implementation directory. At the very least, even if I decide not to | |
167 | merge your implementation, I am certainly willing to link to you | |
168 | implementation once it is completed. | |
81f5db50 JM |
169 | |
170 | * You do not need to implement line editing (i.e. readline) | |
171 | functionality for your implementation, however, it is a nice | |
172 | convenience for users of your implementation and I personally find | |
173 | it saves a lot of time when I am creating a new implementation to | |
280688b2 | 174 | have line edit support early in the process. |
c088d7cf JM |
175 | |
176 | --- | |
177 | ||
178 | **Good questions that either don't have answer or need more detail** | |
179 | ||
180 | ### Why do some mal forms end in "\*" or "!" (swap!, def!, let\*, etc)? |