A canonical reference for this type of question is Paul Graham's What Made Lisp Different. The two remaining key features of Lisp that are not widely available, according to this article at the time of its writing, are:
8. A notation for code using trees of symbols.
9. The whole language always available. There is no real distinction between read-time, compile-time, and runtime. You can compile or run code while reading, read or run code while compiling, and read or compile code at runtime.
The commentary addresses each point and names popular languages where that feature is available.
8, which (with 9) is what makes Lisp macros possible, is so far still unique to Lisp, perhaps because (a) it requires those parens, or something just as bad, and (b) if you add that final increment of power, you can no longer claim to have invented a new language, but only to have designed a new dialect of Lisp ; -)
Note that this article was last revised in 2002, and in the past 11 years there have been a wide variety of new languages, some of which may incorporate all these Lisp features into their design.
When type theorists say "typed", they mean a what most programmers call statically typed. This is due to a fundamental divide: Type theorists care about proofs and related beasts, and hence care about statements that apply to all possible executions of a program. The mere notion of a "runtime type tag" doesn't make sense to them. If a type theorist says "this has type int
" they mean "I can formally proof that this only ever takes on int
values".
In contrast, an untyped language is one where you can't create such a proof, because the language doesn't give you enough guarantees/information.
This is the original meaning of "untyped" and it's actively used by (a minority of) people talking about type systems online. An alternative term is "unityped", because if you have to assign a type, you only have the trivial type "any value whatsoever" available.
The simply typed lambda calculus is typed in this sense, it has a static type system as you'd say. In the same sense, both Scheme and the untyped lambda calculus are untyped.
Programmers, on the other hand, primarily want to know what kind of value is in some memory location; whether this knowledge is innate in the source code for a compiler to explore and make use of, or whether it is determined at run time, is a separate decision.
In accordance with their understanding of "type", programmers have a different definition of "untyped": A system that has neither static information nor runtime tags, because there is effectively only "one type" to choose from (e.g. in Tcl, everything is a string). In this sense, the untyped lambda calculus is still untyped (everything is a function), but Scheme is, as you note, typed (though dynamically).
Best Answer
The advantage of hygienic macros is not one of language capability -- you can write macros that have good hygiene using
gensym
and careful quoting/unquoting at the right times. However, hygienic macros ensure your macros have good hygiene. In that respect, it's a bit like type-checking.There may also be tooling advantages to hygienic macros. Most hygienic macro systems impose tight controls on what your macro does and how it does it (e.g., you can't execute arbitrary code when a macro defined by Scheme's
syntax-case
is expanded). This can make it easier to write programs that "understand" your macro and can provide additional tooling support.On the other hand, there are some cases where unhygienic macros may be useful. For example, if you actually want to capture a binding for a specific variable (e.g., anaphoric macros) then I think you're out of luck if you only have hygienic macros.