Parameterized Procedures for Testing, Mocking, Plumbing

By Artyom Bologov

IMAGE_ALT

I'm about to suggest a simple solution to the problem of

From this list, it's more or less evident why one might need this. But I'll explain my context anyway:

The Problem

Biological computations are fun:

So I ran one computation piece that took several hours. It wasn't nice waiting for so long. (Even though I got some delicious cherry ice cream while waiting for it.) I've sworn to optimize it, or otherwise make it bearable to debug.

There was this one function, mph-em, that was taking most of the time. Especially so on first run. It was an important one and there were no corners to cut. I tried:

It wasn't enough. I needed to somehow avoid the long-running computation altogether. And just get the result. I needed to pre-compute the results of mph-em and plug them into the program. But what if it's too deep into the program and I can't feasibly pass anything from outside?

The Solution: Parameterize

Lisps have a long tradition of dynamic vs. lexical binding wars. Lexical bindings won, but there are uses for dynamic bindings. Like temporary procedure/value overriding! So I reached for Scheme's take on dynamic bindings, SRFI-39 Parameter objects and came up with this macro:

;; Define a NAMEd procedure and PARAMETER-NAMEd parameter
;; variable. Proceeds with running BODY with ARGS when PARAMETER-NAMEd
;; variable is #false. When PARAMETER-NAME is `parameterize'd to a new
;; procedure, call this procedure on ARGS instead. Useful to override
;; a procedure (like replacing the results for testing or providing
;; shortcut data for long-running computation.)
(define-syntax define-parameterized
  (syntax-rules ()
    ((_ ((name parameter-name) . args) body ...)
     (begin
       (define parameter-name (make-parameter #f))
       (define (name . rest)
         (apply (or (parameter-name)
                    (lambda args body ...))
                rest))))
    ((_ (name . args) body ...)
     (define (name . args) body ...))
    ((_ name value)
     (define name (make-parameter value)))))
define-parameterized macro allowing to easily override a procedure/variable temporarily

So now I can define-parameterized my mph-em and plug (via parameterize) it in whenever I need it:

(define-parameterized ((mph-em %mph-em) reml? eval x y vg ve b)
  #|...|#)
(parameterize ((%mph-em (lambda args (mtx:alloc #(#|...|#)))))
  (code-calling-mph-em))
;; Or, if you only need it to fire once
(parameterize ((%mph-em (lambda args
                          (%mph-em #f)
                          (mtx:alloc #(#|...|#)))))
  (code-calling-mph-em))
Use of define-parameterized

I was unsure whether this post should've been written altogether. It's quite obvious, right? But it might be just my knowledge bias, so here goes nothing! Tell me whether it's obvious or not, in the feedback form below ↓

And hey, give this macro a go! SRFI 39 is the only dependency and is pretty well supported, so no trouble in trying!

Leave feedback! (via email)