Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

LISP's macros are extremely powerful constructs, and the inability to introspect and modify the program itself beyond the method signature level has always struck me as a limitation. Yet I favour "complex" syntax because it tends to be closer to natural language.

So far I have failed to find a language which combines a powerful macro mechanism such as LISP's with a naturally looking syntax (1). Is anyone aware of such a language?

Footnote:

  1. I would consider python to have a naturally looking syntax as it allows constructs like this: if 0 < a < 5 and b in list. The avoidance of braces to structure blocks is irrelevant in this case, though.
share|improve this question
4  
I certainly do not want this to be an answer, but yes, there is a such language, and it is Perl6. Hygienic macros are part of the language, but no Perl6 compiler has yet implemented it. –  Thaddee Tyl Jul 29 '11 at 16:37
    
You might check out C++ Boost Library Lambda expressions - boost.org/doc/libs/1_47_0/doc/html/lambda.html –  David I Jul 30 '11 at 20:34
1  
Nemerle is such a language. –  Paul Nathan Jul 31 '11 at 16:08
3  
And needless to mention that Lisp itself can be turned into such a language, with a help of one of numerous parsing libraries and reader macros. –  SK-logic Aug 1 '11 at 7:45
    
And another one: convergepl.org –  SK-logic Aug 1 '11 at 7:53

12 Answers 12

up vote 8 down vote accepted

Yes. You should look at David A. Wheeler's readable S-expressions proposal. It provides a more readable way to write Lisp programs, without making any semantics-specific syntax. Thus it's fully general and can be applied to any Lisp dialect or S-expression-based DSL.

Thus, using this new style in Scheme, your example would be written as (with list renamed to lst because list is a Scheme built-in):

if {{0 < a < 5} and {b member lst}}
  ...

The reader takes care of translating that automatically to

(if (and (< 0 a 5) (member b lst)) ...)

without having to know anything about if, and, <, etc.

For programmers from non-Lisp languages, they may still be surprised to learn that the curly brackets are not optional (and cannot be added or removed indiscriminately), but it's still a much smaller learning curve than prefix notation or "all those parentheses".

share|improve this answer
    
Is there an implementation of this already, say in Racket or perhaps Clojure? –  Robert Harvey Jul 29 '11 at 17:24
    
@Robert: There's a reference implementation at sourceforge.net/projects/readable, but I don't know if it works with Racket. (Clojure, alas, already uses curly brackets for something else, so this syntax will conflict with that.) –  Chris Jester-Young Jul 29 '11 at 17:52

Here's a list of non-LISP languages that allow for macros (including those mentioned in other answers) -- the links are to an explanations of the respective macro systems:

Languages that have features that are kind of like macros, or which accomplish more or less the same thing in different ways (namely Smalltalk and Io)):

  • F#
  • Smalltalk
  • Io (Also homoiconic)
  • D

We should note that being natural-language-like and allowing for clear, easily intelligible expression do not always go hand in hand (especially since the latter quality is heavily dependent upon how one trains their ways of thinking and reading). In your python example, 0 < a < 5 does not accord with natural English. In English, we would have to say 0 is less than a, which is less than five or maybe a is greater than 0 and less than five: i.e., we either need a relative clause that uses 'which' to indicate that a is still the subject of our second qualification, or, if we want to use a single main clause, we need to complicate the expression by using the two different predicates 'greater than' and 'less than'. 0 < a < 5 is especially readable because it gives a clear, uncluttered, concise expression of the relation-concept, not because it is accords with natural language.

I'd like to present the Macro system in Prolog, just because I think the language is great for forming clear and illuminating articulations of problems, and I like it. I'll first show how we might write your example in idiomatic Prolog, then I'll show how we can write some rules to get Pythonish syntax for the same expression using the macro system.

Here's a flushed out python line elaborating on your example:

if 0 < a < 5 and b in list:
    print ("that is true!")
else:
    print ("that is not true!")

In Prolog, we might write the conditional thus[1]:

( 0 < A, A < 5, member(B, List) % If 0 < A and A < 5 and B is member of List
->  write('that is true!')      % then write ...
;   write('that is not true!')  % else write ...
)

Prefix notation is common for Prolog predicates[2], but the way it gets used is mostly like the prefix notation in predicate logic. If you've learned the latter, Prolog is very clear, but maybe not if not.

My understanding is that macro expansions are easy to implement in LISPs because they are homoiconic languages: i.e., in LISP, code is data (a language can, obviously, implement macros without being homoiconic, it's just not as straightforward). Prolog is also homoiconic. Prolog accomplishes macro expansions by preprosseing code and pattern matching all the terms of a program with the predicateterm_expansion/2, and it's derivative, goal_expansion/2.

The following bit of code uses operator declarations, op/3 and goal_expansion/2 to get syntax like your Python example.

:- op(200,  yfx, user:(<.)).
:- op(200,  xfx, user:(in)).
:- op(1000, xfx, user:(and)).

goal_expansion(A and B, (A, B)).                % Read: if term matches A and B, replace with (A,B)
goal_expansion(L <. M <. H, (L < M, M < H)).    % `.` added to avoid conflict with the builtin `<`
goal_expansion(E in List, member(E, List)).

example(A, B, List) :-
    0 <. A <. 5 and B in List -> write('This is true').

In action:

?- example(4, b, [a,b,c,d,e]).
This is true

Footnotes

1: I don't know whether this pattern would ever be useful. I'd probably just write a predicate to take care of the condition:

foo(A, B, List) :-      % foo(A, B, List) is true if...
    between(0, 5, A), 
    member(A, List). 

to be used thus:

bar(Var) :-             % Var is bar if...
    <some conditions>,  % conditions are true and
    foo(A, B, List),    % foo(A, B, List) is true and
    ... .               % whatever else is true.

2: In Prolog, most of the work is done with predictes Prolog predicates are the (very) rough equivalent of functions in other languages (with the critical proviso that *they are not functions and do not evaluate to their return value). The definition of a function in Python, with the formdef (): corresponds to the definition of a predicate in Prolog, with the form() :- . Predicates are referred to withpredicate_name/arity`.

share|improve this answer

Scala has both macros and (much!) more complex syntax than any Lisp!

...project Kepler, an ongoing effort towards bringing compile-time metaprogramming to Scala. Our flavor of macros is reminiscent of Lisp macros, adapted to incorporate type safety and rich syntax. Unlike infamous C/C++ preprocessor macros, Scala macros: 1) are written in full-fledged Scala, 2) work with expression trees, not with raw strings, 3) cannot change syntax of Scala.

Macros are functions that are called by the compiler during compilation. Within these functions the programmer has access to compiler APIs. For example, it is possible to generate, analyze and typecheck code. You can learn more about macros from documentation.

Macros are now officially a part of the Scala language. Since 2.10.0 Scala includes macros that can be enabled with import language.experimental.macros on per-file basis or with -language:experimental.macros on per-compilation basis.

Macros significantly simplify code analysis and code generation, which makes them a tool of choice for a multitude of real-world use cases. Scenarios that traditionally involve writing and maintaining boilerplate can be addressed with macros in concise and maintainable way. Therefore we believe that macros are a valuable asset to Scala programming language...

share|improve this answer

I think Nememrle macros are great. They also allow you to change the syntax of the language, which I think more closely resembles macros in LISP than any other non-LISP language.

share|improve this answer

D has a mixin statement that combined with compile time function execution allows defining lisp-style macros. Unfortunately it's limited to complete statements.

share|improve this answer

My "readable" project has been working to solve this exact problem (making Lisp much more readable). You can see the specs, and the code, at http://readable.sourceforge.net. We've devised a set of additional abbreviations for Lisp readers; you don't need to use the additional abbreviations, but if you do, the resulting code is easier to read. The hope is that eventually readers will build them in, but you can also use our reader implementation (MIT license).

We'd love to get feedback, help, etc. Please check us out!

http://readable.sourceforge.net

share|improve this answer
    
As was noted in an earlier comment, the key thing about our "readable" approach is that it's general and homoiconic. Past efforts to make Lisps readable often weren't.. which is why they failed. They're backward-compatible abbreviations, too. For example, they add "curly-infix" lists, normal Lisp lists expressed in infix order. E.G., {n >= 2} is just an abbreviation for (>= n 2). They add "neoteric" expressions so you can say "f(a b)" as an abbreviation for (f a b). And finally, they add indentation as meaningful. –  David A. Wheeler Aug 20 '12 at 4:31
    
btw., have you seen bit.ly/vqqvHU ? It is also general and fully homoiconic (e.g., any expression of any level can be wrapped into a quasiquotation, including the user-defined syntax). –  SK-logic Aug 20 '12 at 8:49

http://www.shenlanguage.org/

Shen is a portable functional programming language that offers

  • pattern matching,
  • λ calculus consistency,
  • macros,
  • optional lazy evaluation,
  • static type checking,
  • an integrated fully functional Prolog,
  • and an inbuilt compiler-compiler.
share|improve this answer
    
I believe shen uses a lisp-like syntax, right? That seems to be the only thing OP doesn't want. –  aBathologist Jan 9 at 20:29

Dylan is said to have a macro system that is comparable to the Common Lisp macro system, while having a "more traditional" surface syntax.

share|improve this answer

F# might fit the bill; although it doesn't have first-class macros, it does have quotations, and you can introspect with reflection.

share|improve this answer
1  
For the record, quotations are described here: msdn.microsoft.com/en-us/library/dd233212.aspx –  blubb Jul 29 '11 at 17:49

I find Boo's macro system nearly as expressive as what you get in Lisp, coupled with a Python-like syntax. Because Boo is statically typed except for the duck-typing features, it's not 100% equivalent, but pretty close in practice, and you get a pretty familiar syntax as a starting point.

share|improve this answer
    
For the record, the macro system is described here: boo.codehaus.org/Syntactic+Macros –  blubb Jul 29 '11 at 17:43
1  
That's one of the alternatives, although that doc is missing the newer happy-path quasi-quoted macro style, which is much more compact and readable. See lostechies.com/ryansvihla/2010/04/05/… for an example or two. –  JasonTrue Jul 29 '11 at 17:49
    
You are still limited by Boo's syntax, while Nemerle allows you to come up with new syntax with the macros. –  Jetti Aug 20 '12 at 14:59
    
Considering you can replace parts of the compiler pipeline (which is easier than it sounds) if that becomes an issue, I don't think that's much of a limitation. Though it's not quite clear what I can do with Nemerle's "syntax extensions" that can't be done with a Boo quasi-quoted macros. –  JasonTrue Aug 20 '12 at 16:36

I don't know for certain, but I'd place bets that the "multi-stage programming" in MetaOCaml is very closely related to Lisp macros.

There's also a metaprogramming variant of Haskell.

share|improve this answer
    
For the record, the Haskell variant is described here: haskell.org/haskellwiki/Template_Haskell –  blubb Jul 29 '11 at 17:55
2  
Macro expansion is a kind of computation at compile time, but syntactic extension (which is what macros do) involves a lot more than just multi-stage programming. (The MacroML paper attempts to connect them, but it uses a severely crippled macro system.) –  Ryan Culpepper Jul 29 '11 at 23:23
    
@Ryan - OK - neither my Lisp nor my MetaOCaml (nor even my ML) knowledge is up to scratch. I did read a bit more after posting this answer, and the three syntactic gizmos (bracketing, escaping and execution) didn't really seem macro-ish in themselves - but what about using them in combination with ordinary functions? A Lisp macro (if I remember correctly) is basically a function, except that it generates code instead of a return value. From that perspective, the multi-layer thing in combination with functions seems like maybe building blocks for Lisp macros. –  Steve314 Jul 30 '11 at 1:35
2  
@Steve314 The problem is that multi-stage programming only lets you manipulate expressions, not variables-to-be-bound or match clauses or any other syntactic category. Only expressions. Furthermore, its type system is too vanilla to let you do things like compute the scope of an expression argument. Imagine trying to implement a new pattern matching syntax... what would the types look like? In contrast (IIUC), Template Haskell does let you represent things other than expressions, and it moves the type system out of the way enough to do useful transformations. –  Ryan Culpepper Jul 30 '11 at 20:54
    
Template Haskell macro system is not as powerful as Lisp, unfortunately. And it enforces a distinctive macro application syntax, which won't allow truly seamless macros into a language. –  SK-logic Mar 23 '12 at 11:02

In Prolog you can create terms/clauses with =.., evaluate them by stating them, or add/remove them from the program using assera, assertz. Although no-one ever accused Prolog of having a natural looking syntax.

You might want to consider Io, as it's reflection capabilities enable you to introspect and rewrite code. I like an old article by why http://web.archive.org/web/20080212010904/http://hackety.org/2008/01/05/ioHasAVeryCleanMirror.html

share|improve this answer
    
assertz/1 is not the prolog equivalent of macro expansion. term_expansion/2(and the derivative goal_expansion/2) are what allow for macro expansions. –  aBathologist Jan 9 at 19:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.