1. 12

  2. 5

    Nice! I think the hashtable syntax for lisp is one of the biggest things “modern” languages get much better.

    I wrote a small set of wrappers around the scheme hash table interface, so I had an API like:

    (define h (make-hash-table))
    ;; instead of
    (hash-set! h "bob" (make-hash-table))
    (hash-set! (hash-ref h "bob") "sally" #t)
    ;; you get
    (ht-force! h "bob" "sally" #t)

    But this just inspired me to make symbols / strings the same.. I wonder what other ways we can make lisp data structure syntaxes that are not lists make more sense. Clojure has certainly made a great example of how bad other lisp’s interfaces are.

    1. 4

      Pardon my french, but holy #@$%! I’ve heard of macros but I never realized/thought they could change the SYNTAX of the language. I always thought they were some kind of improved versions of functions/classes, but this is major.

      I have a question though, for this: How much runtime over head is this? Say, for this particular example, is the compiled code equivalent to simply generating a hash table (i.e. no run-time overhead) or does the string get parsed (i.e. everything in set-macro-character) in runtime?

      1. 3

        Well, that’s the objective of macros, not only to change the syntax but to create new data structures. Reader macros are the simplest ones

        One of the classical macros is to use it to define automatas (and parsers) at compile time, skipping tools like lex/yacc: one simple example in lisp, and something more interesting in Scheme.

        As usual, C2 wiki is always an interesting read.

        1. 3

          I’ve heard of macros but I never realized/thought they could change the SYNTAX

          That is because the OP is about reader macros not macros. Macros are more less what you functions that run at compile time and operate on code (which is made of Lisp’s own objects) or syntax objects in the case of Racket

          1. 2

            Macros are expanded at compile time, so there’s only a difference when the file’s loaded; at runtime, the code will be identical to you constructing the hash table yourself.

            The code here is actually using a reader macro, which means a) the code’s actually more involved here than in most macros, and b) it’s expanded at read time, rather than at compile time. This is necessary because {a => 1} isn’t something Lisp knows how to read in by default, so we need tell it how.

          2. 3

            There are a few demos like this around, but they’re mostly just gimmicks to show the power of macros in Lisp. I don’t think I’ve seen any projects actually using such a library.

            If I need an association container that’s small enough to be expressed as a literal, I almost always just use an association list, which have built in literal syntax and are accessed similar to hash tables. They’re asymptotically slower than hash tables, but for small values of n they end up being faster because there’s less overhead and there’s no hash function to compute, just a series of equality comparisons.

            If an association list really won’t work for what I’m doing, the alexandria library has alist-hash-table which builds a hash table from an association list.

            1. 2

              Could you point me to examples where macros are used, not for effect, but to change syntax to improve code readability in practical code? Thanks!

              1. 6

                I’m going to assume that by macros you mean reader macros[1], there are plenty of examples of useful reader macros:

                • cl-interpol enables string-interpolation.
                • CLSQL provides a reader macro to enable the user to generate easily embed SQL expressions in their code (tutorial)
                • local-time extends the reader to provide syntax for timestamp’s (ej @2016-12-14T16:36:45.346589-05:00 is read as a instance of the timestamp class)
                • Although I can’t find the link I recall Dan Weinreb mentioned that at ITA they used reader macros for Airport codes (important in their domain).
                • Rutils provides hash-table syntax, although I agree with jlarocco on their assessment that alists are better for a small number of items,

                Also borderline practical but definetly cool, Vacietis reader macros to translate C code to CL

                Keep in mind, reader macros are limited in what they can do, they only dispatch on one or two characters, Racket provides a more extensible way to switch readers.

                Edit: I forgot about FSET which provides syntax for sets

                [1] - Which are really unrelated to macros. Macros are more or less functions that run at compile time (technically just before, at macro-expasion time). Reader macros work at read time (more or less parse-time).

                1. 3

                  The tricky thing is that by the time a person knows enough lisp to write such macros they’re usually pretty sold on it, so they’re not in a huge hurry to change the syntax.

                  A popular demo for showing off reader macros is to implement a full JSON reader syntax. kivikakk linked to his, but there’s a more full featured implementation here that also explains how it works.

                  Chapter 4 of Let Over Lambda shows an example that adds Perl-like regular expression “literals”.

                  List Comprehensions similar to Python or Haskell.

                  The standard loop macro isn’t a reader macro in the technical sense, but it’s similar in the way it builds up its own mini language.

                  CLPython does some interesting stuff like making the REPL understand Python syntax. I’m not sure if it’s maintained any more, though.

                  The more “real world” examples of complex macros usually take Common Lisp (or a subset) and transparently convert to something else.

                  Varjo (part of Cepl) converts Lisp to GLSL shaders to execute on GPUs. It’s part of a neat graphics library called Cepl, which has some demos on Youtube.

                  Parenscript converts a subset of CL into Javascript.

                  1. 3

                    A simple but useful class of macros, so common that it’s a CL idiom, is along the lines of,

                    (with-foo X

                    Where foo is some kind of resource: a file, lock, socket, db handle, query result, elements of a datastructure, etc. The macro aquires the resource (if needed), establishes local bindings to refer to it conveniently, and frees/cleans up at the end (if needed).

                2. 2

                  This reminds me of a tiny JSON reader and printer for Common Lisp I wrote in 2012; reuploaded. Sample use:

                  CL-USER> (setf *eg* {"literal": "json syntax", "pretty": {"cool": "I think."}})
                  {"literal": "json syntax", "pretty": {"cool": "I think."}}
                  CL-USER> (gethash "literal" *eg*)
                  "json syntax"

                  I don’t know that it’s actually useful, but it’s a cute demo of what you can do relatively easily in CL.