combinators

In the context of Domain Specific Languages, combinators help capture useful usage patterns (as a higher order function (consumes and/or yields functions) for instance) into a more powerful building block.

Extending the domain specific language then becomes the question of tweaking the usage of these combinators.

For getting the most out of these inclusions, consider using a uniform interface for the constituents and the combinations.

This structural recursion then allows for powerful compositions and potential to express more complex abstractions.

An example for the might be:

  • choose a combinator as the higher order function representing function composition
    • given functions f(x) and g(x), their composition yields another function f(g(x))
  • the domain and range of f(x) and g(x) are established to be a certain set, we can conveniently combine multiple such functions that are further composable down the lane
(defun compose (&rest functions)
  (lambda (argument)
    (reduce (lambda (result fn) (funcall fn result))
            functions
            :initial-value argument)))

(defun square (x) (* x x))
(defun increment (x) (+ x 1))

(funcall (compose #'square #'increment) 1729) 

Another higher order combinator would be the iterative function applier

1. Some Primitive Combinators

1.1. parallel-combine

collates independent executions of different functions on the same arguments

(defun parallel-combine (funcs collator)
  (lambda (argument)
    (funcall collator
           (mapcar (lambda (func) (funcall func argument))
                   funcs))))

(defun square (x) (* x x))
(defun increment (x) (+ x 1))

(funcall (parallel-combine (list #'increment #'square)
                       (lambda (results)
                         (mapcar (lambda (f)
                                   (apply f results))
                                 (list #'list #'+ #'*)))) 1729)
(1730 2989441) 2991171 5171732930

1.2. Spread-combine

Piping the partitioned input into corresponding functions can be a powerful when abstracted away as a higher order function.

One needs to know the arity of each of the constituent functions for this to be constructible.

I'm using SBCL currently so corresponding function to get the arity of a function is sb-introspect:function-lambda-list

(sb-introspect:function-lambda-list
 (lambda (x y z &optional a &rest args) 1729))
X Y Z &OPTIONAL A &REST ARGS

2. Caveats

  • consider generically and intelligently handling the arities of constituent functions for the combinator to be more robustly applicable
Tags::programming: