4 ms·
> And no, you can't have LISP without its syntax, because that's what's giving it power. The "code is data" mantra only works because Lisp is homoiconic No. Ho
by xearl 10y ago
> And no, you can't have LISP without its syntax, because that's what's giving it power. The "code is data" mantra only works because Lisp is homoiconic
No. Homoiconicity is nice for "code is data" and macros, bot not necessary. Being able to reify and generate ASTs is sufficient. See for example Dylan, Terra/Lua, Scala LMS, MetaML, or Julia's or Rust's macros.
- leephillips 10y agoDon't know about the others, but Julia is homoiconic.
- KingMob 10y agoI wish Julia the best after 7 years of having to use Matlab, but despite its metaprogramming facilities, it's not actually homoiconic. It appears enough people pointed this out that they finally stopped using the word on their website.
- leephillips 10y ago"Like Lisp, Julia represents its own code as a data structure of the language itself. Since code is represented by objects that can be created and manipulated from within the language, it is possible for a program to transform and generate its own code. This allows sophisticated code generation without extra build steps, and also allows true Lisp-style macros operating at the level of abstract syntax trees." (http://docs.julialang.org/en/stable/manual/metaprogramming/#macros http://docs.julialang.org/en/stable/manual/metaprogramming/#...) Doesn't this qualify? I may certainly be missing something in my understanding of what "homoiconic" means.
- KingMob 10y agoHomoiconic means that the code structure is similar to the actual AST. In Lisps, this is easy to see, because a nested list of symbols is pretty much the definition of an AST. In truth, real homoiconicity is hard to achieve in any language that doesn't represent code as trees/S-expressions. In Julia, they built a data structure that can represent code, and can be accessed via reflection, but it's no more homoiconic than most languages (note that "homoiconic" is nowhere on the page you linked to). In Lisps, the code is just nested lists, like other list data, and can be manipulated using the all of the standard list-processing functions. Whereas, if you look at the examples in Julia, you'll see that using macros vs functions involves special syntax all over the place. A good page for this is http://c2.com/cgi/wiki?HomoiconicLanguages http://c2.com/cgi/wiki?HomoiconicLanguages
- leephillips 10y agoI see - you're exactly right. Thanks for the clear explanation.
- KenoFischer 10y agoUsually I avoid splitting hairs, but since the terminology bikeshed is all about that, let me make a little correction. In julia, there is a data structure called `Expr`, which is what's used internally by the compiler to represent code and is exposed to julia. It's not a list, it's a tree, but the power of being able to access it like any other tree still remains. Of course julia's surface syntax is significantly more complicated than lisp, so macros are more complicated.
- bad_user 10y agoNote sure what you're arguing against. I stand by my claim. I'm actively working with Scala's macros and played with Scala LMS. In fact working with those convinced me of how much all LISP alternatives suck for expressing macros. Do you know why? Because in these languages macros are not first class, hence (1) they tend to be an afterthought, (2) hard to use and (3) filled with bugs because, wouldn't you know, they are targeting "library authors" and not users. Scala's macros for example are exposing the compiler's internals. And boy, I can tell you stories. Like how it wants an "untypecheck" call on the AST if you modify it, because those ASTs happen to be mutable (the horror), but then "untypecheck" has a bug in it, crashing the compiler if you have an implicit "unapply" in a pattern match, so you have to rewrite your AST first to get rid of implicit "unapply" calls to work around it. Behold Sincron, an otherwise simplistic library, for which it took me a whole month to achieve inlining Function0 literal arguments, with help from others and copy/pasted code, between cries that I crashed the compiler, again and again: https://sincron.org https://sincron.org - and btw, this rewrite for getting rid of unapply, this was the still-dangerous shortcut I took, because the general consensus at this point is that if you want to workaround the bugs of "untypecheck", you need to fix that AST manually. You can't blame Scala too much really. This is definitely preferable to hacking your own compiler plugin or to forking the compiler. And eventually beautiful solutions can emerge from that. The alternative to something like this is to expose a limited form of macros. For example F# quotations, or Rust's macros, since you mentioned those. The problem with these macro systems is that they are very limited, only applicable to a narrow set of use-cases. Sorry, but I personally think Rust's macros are next to useless as they needlessly complicate the language without that big of a gain. And the irony is that macros are basically used for error-handling in Rust. You know, if they added higher-kinded types, there wouldn't have been such strong demand for those macros in the first place. LISP is the only language (family) exposing macros to users.
- steveklabnik 10y agoThe standard library contains many macros that are not about error handling: http://doc.rust-lang.org/stable/std/#macros http://doc.rust-lang.org/stable/std/#macros Only try! is, specifically. vec!, write!, panic!, and println! are also very popular.
- 10y ago