Skip to content

Instantly share code, notes, and snippets.

@rocketnia
Created October 25, 2010 06:32
Show Gist options
  • Save rocketnia/644503 to your computer and use it in GitHub Desktop.
Save rocketnia/644503 to your computer and use it in GitHub Desktop.
A short, comprehensive curry operator for Arc.
; curry.arc
; Copyright 2010 Ross Angle
;
; This code is released under the
; Perl Foundation's Artistic License 2.0.
; This code is an example for a forum post at
; <http://arclanguage.org/item?id=12599>.
; We're going to overwrite '< and '> as a demonstration, so we need to
; avoid relying upon them in the currying code itself.
(= ac-dot-scm-< <)
; We define a new type for functions that know two arities: How few
; arguments they should be able to take without resorting to currying
; (which can be 0 in order to suppress automatic currying altogether)
; and how many arguments they should build up when curried before they
; finally "trigger" and give those arguments to the wrapped function.
;
; The 'curry-trigger-arity isn't necessarily a maximum; if a partially
; curried function is passed ten arguments at once, it'll try to pass
; all ten of them to the underlying function (along with the rest of
; the accumulated arguments) rather than leaving any out or raising an
; error of its own. The wrapped function may still react to a dozen
; arguments by raising an error.
;
(def curryable (min-noncurried-arity curry-trigger-arity fn)
(annotate 'curryable
(list min-noncurried-arity curry-trigger-arity fn)))
; Usually, 'curryable functions work just like their underlying
; functions. However, when they're called with fewer than a certain
; number of arguments, they automatically curry themselves (as
; explained above).
;
; NOTE: Official Arc doesn't support 'defcall, but Anarki and Rainbow
; do.
;
(defcall curryable (self . args)
; NOTE: Rainbow automatically unwraps 'self here, whereas Anarki
; currently doesn't.
(case type.self curryable nil
(zap [annotate 'curryable _] self))
(let (min-noncurried-arity curry-trigger-arity fn) rep.self
(if (ac-dot-scm-< len.args min-noncurried-arity)
(apply cur self args)
(apply fn args))))
; The currying behavior of 'curryable functions can be selected
; explicitly by passing them to 'cur. The 'cur function can also take
; an integer value and any function-like value as two arguments, in
; which case it'll return a function that curries the function-like
; value using the given integer as an explicit 'curry-trigger-arity.
;
; The 'cur function itself acts as a curried function, just to stay on
; theme. It can be called with just a single integer, with no
; arguments, or with some extra arguments to pass along to the curried
; result.
;
(def cur args
(iflet (trigger-determiner . rest) args
(case type.trigger-determiner
int (.rest:afn (fn-and-args)
(if (ac-dot-scm-< trigger-determiner
len.fn-and-args)
(iflet (fn . args) fn-and-args
(apply fn args)
(err:+ "A curried call was ready before even "
"the function was collected. This "
"indicates a negative trigger arity "
"was explicitly provided."))
(fn more
(self:join fn-and-args more))))
curryable (let (min-noncurried-arity curry-trigger-arity fn)
rep.trigger-determiner
(apply cur curry-trigger-arity fn rest))
(err "An unsupported type of value was passed to 'cur."))
cur))
; At this point, we can usually overwrite existing global functions
; with 'curryable versions. However, other global-overwriting
; utilities like 'extend might replace them with regular functions
; again.
(zap [curryable 2 2 _] <)
(zap [curryable 2 2 _] >)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment