I’m looking for a language that will let me do what I could do with Clipper years ago, and which I can do with Tcl, namely add functionality in a way other than just adding functions.
For example in Clipper/(x)Harbour there are commands #command, #translate, #xcommand and #xtranslate that allow things like this:
#xcommand REPEAT;
=> DO WHILE .T.
#xcommand UNTIL <cond>;
=> IF (<cond>);
;EXIT;
;ENDIF;
;ENDDO
LOCAL n := 1
REPEAT
n := n + 1
UNTIL n > 100
Similarly, in Tcl I’m doing
proc process_range {_for_ project _from_ dat1 _to_ dat2 _by_ slice} {
set fromDate [clock scan $dat1]
set toDate [clock scan $dat2]
if {$slice eq "day"} then {set incrementor [expr 24 * 60]}
if {$slice eq "hour"} then {set incrementor 60}
set method DateRange
puts "Scanning from [clock format $fromDate -format "%c"] to [clock format $toDate -format "%c"] by $slice"
for {set dateCursor $fromDate} {$dateCursor <= $toDate} {set dateCursor [clock add $dateCursor $incrementor minutes]} {
# ...
}
}
process_range for "client" from "2013-10-18 00:00" to "2013-10-20 23:59" by day
Are there any other languages that permit this kind of, almost COBOL-esque, syntax modification?
If you’re wondering why I’m asking, it’s for setting up stuff so that others with a not-as-geeky-as-I-am skillset can declare processing tasks.
10
There are many languages out there that are quite flexible. Many (often newer) languages pride themselves in their suitability for domain-specific languages (DSLs). However, some of your examples need some macro system.
Some languages that prominently use macros:
- Lisp and descendants. These work as abstract syntax tree (AST) substitutions.
-
C, C++. The C preprocessor uses token-level subtitution, and is less powerful than Lisp macros. Your first example can be directly translated to C macros:
// untested #define repeat for(;;) { #define until(cond) if(cond) break; } auto n = 1; repeat { n++; } until(n > 100)
Many languages allow functions to be called with keywords. Examples include
- Lisps like Common Lisp.
- Smalltalk and descendants.
- Python:
-
Some languages have syntax that allow to fake named arguments, e.g.
-
many command-line interfaces:
git commit --file=foo.txt --message="Some fixes" --author=me
- Any language with dictionary literals
- Perl.
- Javascript:
foo.bar({ x: 42, y: somethingElse })
.
-
Languages that have function literals/lambdas/closures don’t need macros to define new control flow.
- Any functional language, e.g. Lisps, ML
- Smalltalk and descendants. Especially noteworthy is Ruby with its ubiquitous
do ... end
blocks. - With clumsy syntax: C++11, Perl, Javascript.
There are some languages that have a flexible view on operators, so that a lot of punctuation like parens for function calls etc. can be left out. An example would be Scala.
Lisp macros are probably a good examples of the kind of feature you are looking for. They basically allow you to extend the language in relatively arbitrary ways.
Effectively, a macro is a function that gets executed at compile time, and can produce whatever code you like. Lisps are well suited to this kind of technique because they are homoiconic – which means that code is itself expressed in Lisp data structures. So your macro just needs to produce a data structure that represents the code that you want.
Here’s an example from Clojure that adds a C-style for
loop to the language:
(defmacro for-loop
"Runs an imperative for loop, binding sym to init, running code as long as check is true,
updating sym according to change"
([[sym init check change :as params] & code]
`(loop [~sym ~init value# nil]
(if ~check
(recur ~change (do ~@code))
value#))))
(for-loop [i, 0, (< i 10), (inc i)]
(println i))
=> prints the numbers from 0 to 9 inclusive as you would expect
As you can see – it only takes a few lines of code to add a new language construct.
One caveat: this is a powerful technique and should be used with care. In general, you shouldn’t use a macro when a normal function would do the job.