The syntax of a language is the primary interface to that language. SyntacticAbstraction allows the programmer to make the syntax easier to use, in the same way that data abstraction makes data easier to use. Languages supporting programmer extensible syntax: * LispLanguage family, exemplified by CommonLisp, SchemeLanguage and DylanLanguage. For some examples, see LispMacro''''''s. "OnLisp" might address this. * ForthLanguage (including PostScript) - all control constructs are "just" words that manipulate the stack and machine state in special ways. You can define your own "words" that offer alternative control constructs. * SmalltalkLanguage - you can pass blocks to functions and invoke them in arbitrary ways. * EDS "OWL" (an internal proprietary language; similar to Smalltalk) - all language statements were simply instances of particular kinds of objects, so you could extend the syntax by defining and using new classes. Also, strategies for a class' response to messages (IE: methods and properties) were extensible: The release version of OWL included several "expert system" method strategies that used a rule-based approach to respond to method calls, rather than procedural code. * C++ allows you to redefine operators, but is otherwise very limited in the "extensible syntax" dimension. Examples of where SyntacticAbstraction is useful: * To concisely express how to read or write data. E.g. ErlangBitSyntax * To express iteration constructs. E.g. ListComprehension''''''s or foreach loops * To provide new control structures E.g. aif below ----- Ok, here's a question for the SmalltalkerLanguage programmers. In OnLisp one of the common idioms is to have a function return false on non-fatal error and some other value otherwise. So a common idiom to deal with this sort of function is (let ((result (function-that-can-return-#f))) (if result (do-something-useful result) (carry-on))) In C-ish pseudo code: result = function_that_can_return_#f if (result) { do_something_useful(result); } else { carry_on(); } One of Paul Grahams macros in OnLisp is called aif. It simplifies this idiom by getting rid of the let. You can write (aif (function...) (do-something-useful it) (carry-on)) However, its real power is because it creates a new variable in the current scope, so you can refer to other variables in the current scope: (let ((variable a-value)) (aif (function...) (do-something-useful it variable) (carry-on))) This is useful in many languages. For example, in Java when one reads from a file you have to check for null. Say you are processing each line with a regexp: Regexp csv = new Regexp("((.*),)*"); String line = bufferedReader.readLine(); if (line != null) { getValues(regexp, line) } else { carryOn(); } You can write this easily with aif: Regexp csv = new Regexp("((.*),)*"); aif (bufferedReader.readLine()) { getValues(regexp, it) } else { carryOn(); } This is something you can't do with blocks. It's not emulating LazyEvaluation but introducing a new binding in the existing scope. However, I have a feeling that this idiom isn't appropriate for SmalltalkLanguage as lexical scoping isn't such a widely used feature of the language. Thoughts? Is this an example of a SyntacticAbstraction that isn't applicable across all languages? ----- I'm not sure I understand the usage or that I've encountered the desire to do it in Smalltalk. In general I've come to feel that language extension as commonly done in Lisp is less appropriate in most other languages. But for this one, why not something like function: arg onSuccess: aBlock (function: arg) ifTrue: [aBlock value] used as in someone function: foo onSuccess: [self doSomethingUseful] ''This isn't the same. I've expanded the example above to show how adding the binding to the current scope is useful'' How about this? (I don't know any Smalltalk worth mentioning, so the details may be wrong.) try: funcBlock whenResultSatisfies: aPredicateBlock do: aBlock |result| result := funcBlock value. (aPredicateBlock result) ifTrue: [aBlock result] bufferedReader try: [self readline] whenResultSatisfies: [:it | it isOK] do: [:it | getValues from: it matching: regexp] -- GarethMcCaughan (a fan of Lisp macros but not terribly excited by anaphora) ---- ''I'm trying to get a discussion on the uses of syntactic abstraction and where it is appropriate rather than how to do it in any particular language. So what excites you about Lisp macros? --NoelWelsh'' Two things, mostly * The fact that they allow control of the time, and number of times, ov evaluation of their arguments, thus allowing you to write control structures on equal footing to those provided by the language designer * The possibility of performing arbitrary computation at read (usually compile) time. This allows you to decide how the macroexpansion rewrites itself as a function of its arguments (i.e. more than a simple textual transformation). e.g. One often sees this sort of things in macros: (defmacro foo (arg1 arg2) `(progn ,@(some-complex-calculation arg1) ,@(another-complex-calculation arg2))) or (defmacro foo (list) `(progn (foo-something (mapcar #'blah ,list)))) Does that help, or am I still being too abstract? --AlainPicard ''So you do this to avoid runtime computation, in the same way some C++ templates try to do computation at compile time via template expansion?'' ---- Does the ability in PerlLanguage to have source-code filtering through another perl program, used to approximate in perl5 some of the syntax of perl6, or to implement (effectively) a compiler for a new language to perl, count as an instance of SyntacticAbstraction? -- MartinRudat I dunno, but PerliGata seems pretty darned impressive. -- JonathanTang ---- CategoryAbstraction