Created
October 25, 2010 06:32
-
-
Save rocketnia/644503 to your computer and use it in GitHub Desktop.
A short, comprehensive curry operator for Arc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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