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)):
- Io (Also homoiconic)
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!")
print ("that is not true!")
In Prolog, we might write the conditional thus:
( 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, 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 predicate
term_expansion/2, and it's derivative,
The following bit of code uses operator declarations,
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').
?- example(4, b, [a,b,c,d,e]).
This is true
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),
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 form
def name(args): body corresponds to the definition of a predicate in Prolog, with the form
name(args) :- body.. Predicates are referred to with