Skip to content

Instantly share code, notes, and snippets.

@dsrkoc
Created May 3, 2012 00:46
Show Gist options
  • Select an option

  • Save dsrkoc/2582238 to your computer and use it in GitHub Desktop.

Select an option

Save dsrkoc/2582238 to your computer and use it in GitHub Desktop.
Attempt at implementing the `uncurried` method for all Closures, focus on enforcing single parameter functions
class ClosureCategory {
static Closure uncurried(Closure orig) {
def nestingLen = checkCurried(orig, 1)
nestingLen == 1 ?
orig :
{ ...args ->
if (args.size() != nestingLen)
throw new IllegalArgumentException(
"Arity does not match, expecting $nestingLen parameters, got ${args.size()}")
else
args.inject(orig) { cl, arg -> cl(arg) }
}
}
// Each nested function in a properly curried Closure
// must accept one parameter and return its inner Closure.
// The innermost function won't return Closure, obviously.
private static int checkCurried(Closure cl, int nestingLength) {
if (isArity1(cl)) {
def (resultTypeClosure, clResult) = returnsClosure(cl)
resultTypeClosure ?
checkCurried(clResult, nestingLength + 1) : // going deeper
nestingLength // bottom maybe reached
} else {
throw new UnsupportedOperationException('Closure is not properly curried')
}
}
private static boolean isArity1(Closure cl) {
cl.maximumNumberOfParameters == 1
}
// a hack to get the Closure's return type,
// and access the inner function
private static List returnsClosure(Closure cl) {
try {
def res = cl(null)
[res instanceof Closure, res]
} catch (ignore) {
[false, null]
}
}
}
// the test ----------
def c1 = { a -> { b -> { c -> a + b + c }}},
c2 = { a, b -> a + b },
c3 = { a -> a + 1 }, // treated as trivially curried
c4 = { -> 1 },
c5 = { a, b -> { c -> a + b + c }},
c6 = { a -> { b, c -> a + b + c }},
c7 = { ...xs -> xs.sum() } // counts as one parameter?
class X { def i }
class Y { def j }
def x = new X(i: 1),
y = new Y(j: 2),
m = [k: 3]
def c8 = { X a -> { Y b -> { Map c -> a.i + b.j + c.k }}}
def expectFail(exClass, action) {
try {
action()
throw new IllegalStateException("Expected failure with exception $exClass")
} catch (e) {
assert e.getClass() == exClass
}
}
use(ClosureCategory) {
assert c1.uncurried()(1, 2, 3) == 6
assert c1.uncurried().curry(1, 2)(3) == 6 // partially apply args
expectFail(IllegalArgumentException) { c1.uncurried()(1, 2) }
expectFail(IllegalArgumentException) { c1.uncurried()(1, 2, 3, 4) }
expectFail(UnsupportedOperationException) { c2.uncurried() }
assert c3.uncurried()(1) == 2
expectFail(UnsupportedOperationException) { c4.uncurried() }
expectFail(UnsupportedOperationException) { c5.uncurried() }
expectFail(UnsupportedOperationException) { c6.uncurried() }
assert c7.uncurried()(1, 2, 3) == 6
assert c8.uncurried()(x, y, m) == 6
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment