I suspect, that, much of the time, when people say that X concept doesn’t work well, or is bad, they may only have exposure to a portion of the potential design space of X. Possibly because popular languages do X in an annoying way, or maybe the design space of X hasn’t been as thoroughly explored as it should be. Of course, X isn’t necessarily good, but just because most things do X in a certain bad way, doesn’t mean that X is bad. Two things that I suspect fit this mold: OO and static typing.
So, I’m working on a new esolang. I think it’s pretty cool, but the name I gave it uses a swear. I want to put my code for this thing on GitHub and talk about it here, but potential employers may see it. I don’t know how to deal with this, besides renaming it.
Suppose I have a macro, let’s call it aif, and within the macro I want people to be able to use the name it. Here’s how I might write such a macro in Clojure (note that this is only an example. if-let is probably a better thing to use in this particular use-case. Also note that I did not test this code).
(defmacro aif [test then else]
`(let [~'it ~test]
(if ~'it ~then ~else)))
This code is usable as such:
(aif (+ 2 2) (println it) (println "false"))
Within the forms provided to aif, a lexically bound variable “it” is visible. Within this post, I shall call things similar to “it” an anaphor.
Note how within the quasiquote (`) I use ~’ to prevent it from being expanded into some-namespace/it. let will only accept non-namespaced symbols, such as it, rather than a symbol such as sandbox/it or clojure.core/it, and normally, with quasiquote, bare symbols will get a namespace stuck onto them. The ~’ will prevent the namespace from being stuck on.
But suppose I have two anaphoric macros that I want to use together, that both expose an anaphor with the same name. There is no way to choose between them. It is also at times counterintuitive, if I’m exposing a function as an anaphor, that I don’t use that function the way that I would use any other function that a namespace provides.
One solution to this might be to not use anaphoric macros of this form (other anaphoric macros that do things other than provide a few names to use within their bodies are not under discussion in this post). For example, aif could be written differently, so as to accept a name. clojure.core includes such a macro, called if-let. However, as pointed out to me by amalloy on IRC, this could make some macros inconvenient to use. recur is, in a sense, an anaphor of loop.
A second solution, the one that I’m proposing, is to allow let to accept namespaced symbols. The form would still only be lexically binding, but could mean that the anaphor would be treated just like any other namespaced symbol. Suppose I’m written my aif macro in the aif namespace. So let’s say, instead of using `(let [~’it ~test] …) I used `(let [it ~test] …) or `(let [aif/it ~test]) (the latter two are equivalent). Then, users might use it as aif/it, or they may (require ‘[aif :as abc]) and then, within the macro, use abc/it. Or they may (use ‘aif), in which case they use it as before.
It annoys me when people try to translate the concept of monads from Haskell to their language of choice, without considering the idioms of the language receiving the translation. For example, in Haskell, variadic functions are limited and difficult, while in Clojure, they’re rather easy to deal with. There is no apply function in Haskell, but there is one in Clojure (apply essentially takes a function and a list, and calls the function, treating the list as arguments to be passed to the function). As such, there is liftM2, liftM3, etc., taking differing amounts of arguments, in Haskell. No good way to write a generalized liftMn. But in Clojure, liftMn can be written cleanly as a function. But, clojure.algo.monads does it as a macro, despite the fact that this is not necessary.
Factor’s list-monad’s return returns a list. This would seem to make sense, except Factor does not define length on lists. This can be annoying in some circumstances. It’s as though whoever was translating into Factor thought that the interesting property of the list monad is related to the view of lists as cons cells. It is not. The interesting property is the view of a bunch of items as being multiple-choice. Arrays would serve the purpose well, and do have length defined on them.
A problem with Factor’s dynamic scoping mechanism, pointed out by Chris Double, is that opening a new scope makes code inside that scope behave differently from if a new scope wasn’t opened. This could potentially be confusing behavior. It would be nice if with-variables and with-variable behaved in such a way that setting variables not mentioned by with-variables/with-variable behaved as though there was no new scope on the namestack.
Got comments working using Disqus. Was apprehensive because I didn’t think it would allow guests to comment, but it turns out it does if I enable the correct option.
I’ve taken an interest in the Factor programming language again. I’m not entirely sure why. I really like the environment, the code reloading that helps prevent some confusion that other languages with code reloading often face, the resumable exceptions, etc. I’m still scared of stack shuffling. And it occurs to me that I don’t see a way to, at the point of a non-resumable exception, edit the code at the surrounding point, ala Smalltalk. It looks like that could, in principle, be implemented, but I think it currently isn’t.
Also, my obsolete Linux system (Kubuntu 10.10) doesn’t like the Factor binary, so I tried to compile from source, but it needs a pre-existing Factor image, and there’s something wrong with the system that usually hosts that.
This was a source of confusion for me going into Clojure from Common Lisp, and also breaks at least one library that was intended to be a direct port from Common Lisp into Clojure.
Symbols in Common Lisp are different things from symbols in Clojure.
Say you have the following code:
. In Common Lisp, it will be read as a symbol. The current package will be stored as part of the symbol. FOOBAR is equivalent to CL-USER::FOOBAR, assuming that FOOBAR is being read in the CL-USER package. In Clojure, by contrast, a symbol can lack a namespace. user/foobar is a distinct symbol from foobar.
The concept of a Common Lisp symbol more closely corresponds to Clojure vars. A Common Lisp symbol directly points to some value. A Clojure symbol can be (if not lexically shadowed) resolved to a var, which then holds the value.
Initially, I thought the Common Lisp way made more sense. I am (possibly irrationally) concerned about the possibility of namespace conflicts. With the Common Lisp way, if there’s two packages with the same name, in theory I could load one in, and all code that it depends on, and then somehow rename the package, and all symbols, since they refer to the package, will keep operating correctly.
However, Clojure’s treatment of symbols, plus the way syntax-quote acts, provides greater macro hygiene, which makes up for the fact that Clojure is a Lisp-1.
Consider the code
`(first [1 2 3])
. This will be converted into the list
(clojure.core/first [1 2 3])
(assuming that some other namespace’s first isn’t shadowing clojure.core’s first. I don’t think that lexical bindings are taken into consderation, but not certain). If a macro emits
(clojure.core/first [1 2 3])
, and a user of the macro lexically binds first to something, there is no interference, as the symbols are different (first vs. clojure.core/first). This contrasts with Common Lisp, where the symbols would be the same, but most of the time there is no conflict because the macro’s first refers to a function, but the user’s code binds a value. Same symbol, resolved differently. However, if the Common Lisp user uses flet or labels around the macro, this could cause a conflict.
EDIT: Bike on Freenode messaged me with some problems he noted with the post. The only one I understand is pointing out that Common Lisp symbols can, in fact, not have a package. However, including such a symbol as code involves using #: (e.g. #:FOOBAR ), which will make a distinct symbol each time it’s read.
<Bike> “FOOBAR is equivalent to CL-USER::FOOBAR, assuming that FOOBAR is being read in the CL-USER package” incorrect given use lists, e.g. (symbol-package (read “sort”)) => #<package CL> (assuming *package* use’s CL-USER, and some other garbage about home packages)
Unfortunately, I don’t entirely understand what this means :(
There are almost no good recursive macroexpanders for Clojure. clojure.tools.macro/mexpand does not correctly deal with macros that use &env, clojure.walk/macroexpand-all incorrectly macroexpands all lists that it sees (meaning that a macro inside a (quote) form will get macroexpanded), and macroexpand-all and macroexpand-most fail when lexically shadowing a macro’s name with something else (I haven’t tested mexpand with that, I think).
Using Ambrose SB’s analyze, however, seems to work properly. It dealt with all the issues I thought to throw at it, although admittedly that’s not much. Just the lexical shadowing and use of &env. However, it seems to be emitting fn forms, despite the fact that fn is a macro, and fn* is the special form. I guess I’ll just have to wait for a reply on this issue.
EDIT: “in other words, it’s an intentional bug, but I’ll fix it.”
EDIT 2: The one thing that would make me thrilled with it is the ability to pass in an arbitrary &env