Live data from Hacker News

Debugging compilers in Clojure

jpmonettas.github.io

1–10 of 14 posts

Re: Debugging compilers in Clojure

#4
This is a nitpick, but I think this is a bad mental model of most lisps:

> Since the compilation unit of most Lisps is a form instead of a file like on most other languages, the core of the ClojureScript compiler can be seen as a program that will take a string representing a Clojure form as input, read it, recursively parse it into a tree of expressions also known as an AST (abstract syntax tree), and then walks down the tree emitting strings containing JavaScript code.

There might be a string representation of the code, but one thing that’s unique about a lot of lisps is that the input to compile/eval is not text: there is usually a function called something like “read” that turns the textual format into normal lisp objects and then eval can take any lisp object and evaluate it. Typical lisp objects are “self-quoting”, but what characterizes a lisp is that the domain and codomain of eval are the same type.

Re: Debugging compilers in Clojure

#5
I recently joined a team where I need to sift through copious amounts of very confusing code. The company is a startup, and many elements were designed hastily. The Flowstorm debugger has proven to be an invaluable tool; my life would have been significantly more difficult without it. I highly recommend it. Also, Juan is an incredibly friendly person, he is clearly very knowledgeable and always eager to help. There's a #flow-storm channel in Clojurians Slack.

Re: Debugging compilers in Clojure

#7

This is a nitpick, but I think this is a bad mental model of most lisps: > Since the compilation unit of most Lisps is a form instead of a file like on most other languages, the core of the ClojureScript compiler can be seen as a program that will take a string representing a Clojure form as input, read it, recursively parse it into a tree of expressions also known as an AST (abstract syntax tree), and then walks dow…

Your core point is absolutely true about how Lisp is special in that it usually provides a read procedure to turn a textual type into a native object that can be evaluated (this is a side effect of homoiconicity, so any homoiconic language will have this property too), but I have one additional nitpick to make ontop of yours:

> [...] eval can take any lisp object and evaluate it.

eval cannot be generalized to accepting any Lisp object, only specifically symbolic expressions (symbols, or lists (potentially nested) of symbols). I discovered this because I thought Chibi Scheme was throwing a warning for valid code[0] to inject a value into an expression for eval, but Marc helped me understand that the warning was correct, because Scheme only specifies what eval does for symbolic values.

[0] https://github.com/ashinn/chibi-scheme/issues/902

Re: Debugging compilers in Clojure

#8

Awesome work, super pumped for this. I wish the Clojure devs would release a first party debugger, almost every other lisp has one built in.

I doubt that would happen due to the focus on core libraries and tooling being platform agnostic, and deferring users to host tooling (and libraries) whenever possible.

The difference between debugging and profiling Clojure and ClojureScript is night and day. e.g. async-profiler and VisualVM are much better than whatever I've seen in the Node.js and browser ecosystems. But tools like FlowStorm seem to be helping close this gap, if you decide to use the custom compilers designed for it.

As someone whose day job is developing with ClojureScript, I've been keeping a close eye on developments in FlowStorm, and I always send this video to get people interested: https://www.youtube.com/watch?v=4VXT-RHHuvI

Re: Debugging compilers in Clojure

#9
post #7

This is a nitpick, but I think this is a bad mental model of most lisps: > Since the compilation unit of most Lisps is a form instead of a file like on most other languages, the core of the ClojureScript compiler can be seen as a program that will take a string representing a Clojure form as input, read it, recursively parse it into a tree of expressions also known as an AST (abstract syntax tree), and then walks dow…

Your core point is absolutely true about how Lisp is special in that it usually provides a read procedure to turn a textual type into a native object that can be evaluated (this is a side effect of homoiconicity, so any homoiconic language will have this property too), but I have one additional nitpick to make ontop of yours: > [...] eval can take any lisp object and evaluate it. eval cannot be generalized to accepti…

Scheme might be different here, I'd have to read the standard, but it's true of Common Lisp at least. You have to distinguish here, I think, between what's valid input to eval and the evaluation rules for given inputs.

Anyways, here's CL's spec for eval[1]:

    eval form => result*
Form is defined in the glossary as "form n. 1. any object meant to be evaluated. 2. a symbol, a compound form, or a self-evaluating object."[2] And you can see this is an exhaustive categorization of lisp objects from the definition of self-evaluating object: "self-evaluating object n. an object that is neither a symbol nor a cons. If a self-evaluating object is evaluated, it yields itself as its only value."[3]

IMO, the useful definition of homoiconicity is "the domain and codomain of eval are the same type". One of the "results" of eval might be that the evaluation routine signals error, if the object has a special evaluation rule.

EDIT: I just read that github thread more carefully, and I guess Scheme's eval is stricter and so, IMO, Scheme is less "homoiconic" :) FWIW, my experience is that Scheme pioneered a lot of things that are more similar to non-lisps than to the more lispy lisps: e.g. schemes typically seem to prefer batch-style compilation to REPLy environments. Source as files vs. image-based development and the standard defines the language in terms of text rather than datastructures.

[1] http://www.lispworks.com/documentation/HyperSpec/Body/f_eval...

[2] http://www.lispworks.com/documentation/HyperSpec/Body/26_glo...

[3] http://www.lispworks.com/documentation/HyperSpec/Body/26_glo...

Re: Debugging compilers in Clojure

#10
post #7

Earlier quoted context omitted.

Your core point is absolutely true about how Lisp is special in that it usually provides a read procedure to turn a textual type into a native object that can be evaluated (this is a side effect of homoiconicity, so any homoiconic language will have this property too), but I have one additional nitpick to make ontop of yours: > [...] eval can take any lisp object and evaluate it. eval cannot be generalized to accepti…

Scheme might be different here, I'd have to read the standard, but it's true of Common Lisp at least. You have to distinguish here, I think, between what's valid input to eval and the evaluation rules for given inputs. Anyways, here's CL's spec for eval[1]: eval form => result* Form is defined in the glossary as "form n. 1. any object meant to be evaluated. 2. a symbol, a compound form, or a self-evaluating object."[…

> Scheme might be different here [...]

Yep, I meant to specify that a subset of your original claim is true for all Lisps, not that your claim was not true for certain ones like CL.

> IMO, the useful definition of homoiconicity is "the domain and codomain of eval are the same type".

I do not think that is a useful definition. Under that definition, any language can be made homoiconic by providing a library with an eval procedure that accepts an AST where nodes in the tree could be non syntax objects, and their value is just accepted as-is for evaluation.

The most useful definition of homoiconic IMO is that the internal representation of the language (the AST) is the same as the external representation of the language (the literal text you write). In that sense, CL and Scheme are equally homoiconic, but Python, C, etc. which you could provide an evaluator for with the same domain and codomain types, are not homoiconic.

Post reply on HN