Hacker News new | ask | show | jobs
by cjo 4439 days ago
What I think this is missing which was a huge part of the SICP course is that Lisp is probably the best language to write your own language in with a custom syntax tailored for the problem space you're working on. Even if it isn't trivial to do.

An example is Clojure's Hiccup[0] where there's a whole new syntax for dynamic HTML generation. It's not a full new Turing-complete language, but it's a custom syntax and vocabulary that illustrates Lisp's extensibility (a sample of Hiccup in action is linked below, though it isn't mine.)

[0] - https://github.com/yokolet/hiccup-samples/blob/master/src/hi...

2 comments

This one of the reasons why, as a Lisp programmer, I don't care much for Clojure. Three different bracketing constructs when one would do just fine. Now I have to go look up the difference between () and {} and [] just to emit some HTML.
No, you wouldn't. Idiomatically [] is used for declarative structures for consumption by macros (like argument lists) and {} is obviously a map, and is used for any key/value pairs. So the HTML DSL linked above is the most intuitive one to a clojure programmer with regard to the data structures used. Adding maps and vectors to be first-level syntax to clojure is one of my favorite things about the language.
As an old Lisp hand, while I'd prefer those declarative structures to be in old fashioned parens, it's not too obnoxious and has some clarity value in adding it as language syntax. But I agree 100% with the value of adding to the first-level/first-class syntax, it's both clear and very convenient.
That's funny. This is one of the reason why I don't care much for Common Lisp. Hashtables in particular are common as mud in programming, but I have to work with a clunky interface specific to each abstraction. Ironically this puts CL closer to Java and C++ than the likes of Python and Ruby.
Hashtables are common, if one does not have other choices.

For many uses of hashtables, lists are just as good.

When I look at that sample, what strikes me is how much the "custom syntax" still looks like a bunch of S-expressions.

How much flexibility does LISP really give you here? For example, say I wanted my DSL to use Python-style significant whitespace instead of brackets. Does LISP make that easy, or is my DSL restricted to using stuff that looks like S-expressions?

Racket is somewhat unique in this respect, but it allows you to create dramatically different languages as DSLs.

Consider this example of the experimental "2d" syntax:

  #lang unstable/2d racket
  (require unstable/2d/cond)
 
  (define (same? a b)
    #2dcond
    ╔═════════════╦═══════════════════════╦═════════════╗
    ║             ║       (pair? a)       ║ (number? a) ║
    ╠═════════════╬═══════════════════════╬═════════════╣
    ║ (pair? b)   ║ (and (same? (car a)   ║     #f      ║
    ║             ║             (car b))  ║             ║
    ║             ║      (same? (cdr a)   ║             ║
    ║             ║             (cdr b))) ║             ║
    ╠═════════════╬═══════════════════════╬═════════════╣
    ║ (number? b) ║          #f           ║   (= a b)   ║
    ╚═════════════╩═══════════════════════╩═════════════╝)
Yes, that big ascii-art graph is actually part of the code, not some sort of comment. The 2d syntax allows you to create ascii art truth tables which contain in them code (in this case, in the traditional paren'd style.)

(See http://docs.racket-lang.org/unstable/2d.html for a better explanation and more examples.)

Alternatively, here is an example of typed racket using sweet expressions:

  #lang sweet-exp typed/racket

  define: fact([n : Integer]) : Integer
    if zero?(n)
       1
       {n * fact{n - 1}}
(https://github.com/takikawa/sweet-racket)*
It might not be easy, depending. Without too much trouble you could write a macro for this:

    (my-macro "
    foo
       bar
    baz beez
       zang
          dang
          bang")
and have the macro parse the string manually into something the eval/apply loop understands. But I think it might get funky pretty quick, and you'll likely lose a lot of power in the process.

The example of Hiccup above doesn't add anything new to Clojure's syntax, the DSL is all legal Clojure data structures. That way it inherits all of the power of the underlying language, like if you want to dynamically generate new HTML - that functionality comes from Clojure. But doing that while modifying the syntax for meaningful whitespace wouldn't be easy.

http://readable.sourceforge.net/

from this:

(define (factorial n)

  (if (<= n 1)

    1

    (* n (factorial (- n 1)))))
to this:

define factorial(n)

  if {n <= 1}

    1

    {n * factorial{n - 1}}
and if you want to use parens, they just work (you can mix and match the two whenever you want without issue). That said, even with such things available, most lisp programmers opt to use parens.