One of the most promoted features of the Lisp dialects is how easy it is to create Domain Specific Languages with them. It is said that a Lisp programmer creates a language for the domain of his problem and then solves the problem as a particular case. That is why a lone developer or a small team can have so much power, because they are quickly dealing with a language customised for them. In my case, one of the domains of my problems is the stock market.
I am writing a very simple application in Scheme, with Gambit-C Scheme, for helping me trade stocks (nothing fancy, I am still an amateur). This application is going to have a comprehensive set of technical indicators which are calculated based on opening, closing, maximum and minimum stock prices as well as the volume traded, number of operations, number of stocks traded etc. Clearly these indicators must be first-class citizens of this small financial world. Once added to the system, I must know how many I have, what is the name the user should see for each of them and the parameters they take. Besides, its syntax must allow users of the system to add indicators of their own without knowing the inner details of the system.
To accommodate those needs I have come up with a little language for my indicators. For instance, these are the Volume and Force Index indicators:
(indicator volume (x) "Volume" (volume x)) (indicator force-index (x) "Force Index" (if (> x 1) (* (- (closing (- x 1)) (closing x)) (volume x)) 0.0)) |
The indicator syntax allows me to give the indicator a user-friendly name, and to automatically add it to my indicator list. But there is something more subtle and interesting about them: the helper procedures. The procedures closing and volume are examples of procedures available to indicator writers. They cannot be lexically scoped, because they depend on the current stock being analised and the current sampling frequency (daily, weekly etc.). They must be bound to the appropriate procedures just in the scope where the indicators are being run. In Common Lisp this is easily done with special variables. In Scheme we have the SRFI-39, Parameter objects. But since the parameter objects act syntatically like closures, using them directly would demand that the indicator writer use a cumbersome syntax like:
(indicator force-index (x) "Force Index" (if (> x 1) (* (- ((closing) (- x 1)) ((closing) x)) ((volume) x)) 0.0)) |
This is very undesirable. The solution, in face of the lack of special variables, is to use a code walker. Due to the unique syntax of Lisp, this whole process can be combined into a simple macro:
(define-macro (indicator name args desc . body) (letrec ((helpers '(opening closing maximum minimum volume num-operations num-stocks num-samples)) (walker (lambda (sexp) (if (pair? sexp) (let ((opr (car sexp)) (args (cdr sexp))) (if (memq opr helpers) (cons `(,opr) (map walker args)) (cons opr (map walker args)))) sexp)))) `(let ((,name (lambda ,args ,@(map walker body)))) (set! *indicator-list* (cons (cons ,desc ,name) *indicator-list*))))) |
And that’s it. From now on I need to think only about indicators, and not where to put them, how to supply them helpers, what’s the frequency of their samples etc. They are high-level abstractions which are now part of my language.
I read similar article also named DSLs are cool, and it was completely different. Personally, I agree with you more, because this article makes a little bit more sense for me