Skip to content

Instantly share code, notes, and snippets.

@luavixen
Created November 27, 2021 00:13
Show Gist options
  • Save luavixen/7d5a6b362378692b13738925168edbff to your computer and use it in GitHub Desktop.
Save luavixen/7d5a6b362378692b13738925168edbff to your computer and use it in GitHub Desktop.
Generic Haskell/functional style currying in Kotlin using extension functions. Example: https://pl.kotl.in/LuUAPZxOV
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
const generateBindImpl = (leftCount, rightCount) => {
const totalCount = leftCount + rightCount;
const genericTypes = [];
for (let i = 0; i < totalCount; i++) {
genericTypes.push('A' + i);
}
const leftTypes = genericTypes.slice(0, leftCount);
const rightTypes = genericTypes.slice(leftCount);
const availableAlphabet = [...alphabet].reverse();
const argumentNames = [];
const boundParameterDeclarations = [];
for (const genericType of leftTypes) {
const identifier = availableAlphabet.pop();
argumentNames.push(identifier);
boundParameterDeclarations.push(`${identifier}: ${genericType}`);
}
const unboundParameterNames = [];
for (let i = 0; i < rightCount; i++) {
const identifier = availableAlphabet.pop();
argumentNames.push(identifier);
unboundParameterNames.push(identifier);
}
const lines = [
`operator fun <${[`T : (${genericTypes.join(', ')}) -> R`, `R`, ...genericTypes].join(', ')}> T.invoke(${boundParameterDeclarations.join(', ')}): (${rightTypes.join(', ')}) -> R {`,
` return { ${unboundParameterNames.join(', ')}${unboundParameterNames.length ? ' -> ' : ''}this(${argumentNames.join(', ')}) }`,
`}`
];
return lines.join('\n');
};
const maxLeftCount = 5;
const maxRightCount = 5;
for (let x = 0; x <= maxLeftCount; x++) {
for (let y = 1; y <= maxRightCount; y++) {
console.log(generateBindImpl(x, y));
}
}
operator fun <T : (A0) -> R, R, A0> T.invoke(): (A0) -> R {
return { a -> this(a) }
}
operator fun <T : (A0, A1) -> R, R, A0, A1> T.invoke(): (A0, A1) -> R {
return { a, b -> this(a, b) }
}
operator fun <T : (A0, A1, A2) -> R, R, A0, A1, A2> T.invoke(): (A0, A1, A2) -> R {
return { a, b, c -> this(a, b, c) }
}
operator fun <T : (A0, A1, A2, A3) -> R, R, A0, A1, A2, A3> T.invoke(): (A0, A1, A2, A3) -> R {
return { a, b, c, d -> this(a, b, c, d) }
}
operator fun <T : (A0, A1, A2, A3, A4) -> R, R, A0, A1, A2, A3, A4> T.invoke(): (A0, A1, A2, A3, A4) -> R {
return { a, b, c, d, e -> this(a, b, c, d, e) }
}
operator fun <T : (A0, A1) -> R, R, A0, A1> T.invoke(a: A0): (A1) -> R {
return { b -> this(a, b) }
}
operator fun <T : (A0, A1, A2) -> R, R, A0, A1, A2> T.invoke(a: A0): (A1, A2) -> R {
return { b, c -> this(a, b, c) }
}
operator fun <T : (A0, A1, A2, A3) -> R, R, A0, A1, A2, A3> T.invoke(a: A0): (A1, A2, A3) -> R {
return { b, c, d -> this(a, b, c, d) }
}
operator fun <T : (A0, A1, A2, A3, A4) -> R, R, A0, A1, A2, A3, A4> T.invoke(a: A0): (A1, A2, A3, A4) -> R {
return { b, c, d, e -> this(a, b, c, d, e) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5) -> R, R, A0, A1, A2, A3, A4, A5> T.invoke(a: A0): (A1, A2, A3, A4, A5) -> R {
return { b, c, d, e, f -> this(a, b, c, d, e, f) }
}
operator fun <T : (A0, A1, A2) -> R, R, A0, A1, A2> T.invoke(a: A0, b: A1): (A2) -> R {
return { c -> this(a, b, c) }
}
operator fun <T : (A0, A1, A2, A3) -> R, R, A0, A1, A2, A3> T.invoke(a: A0, b: A1): (A2, A3) -> R {
return { c, d -> this(a, b, c, d) }
}
operator fun <T : (A0, A1, A2, A3, A4) -> R, R, A0, A1, A2, A3, A4> T.invoke(a: A0, b: A1): (A2, A3, A4) -> R {
return { c, d, e -> this(a, b, c, d, e) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5) -> R, R, A0, A1, A2, A3, A4, A5> T.invoke(a: A0, b: A1): (A2, A3, A4, A5) -> R {
return { c, d, e, f -> this(a, b, c, d, e, f) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6) -> R, R, A0, A1, A2, A3, A4, A5, A6> T.invoke(a: A0, b: A1): (A2, A3, A4, A5, A6) -> R {
return { c, d, e, f, g -> this(a, b, c, d, e, f, g) }
}
operator fun <T : (A0, A1, A2, A3) -> R, R, A0, A1, A2, A3> T.invoke(a: A0, b: A1, c: A2): (A3) -> R {
return { d -> this(a, b, c, d) }
}
operator fun <T : (A0, A1, A2, A3, A4) -> R, R, A0, A1, A2, A3, A4> T.invoke(a: A0, b: A1, c: A2): (A3, A4) -> R {
return { d, e -> this(a, b, c, d, e) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5) -> R, R, A0, A1, A2, A3, A4, A5> T.invoke(a: A0, b: A1, c: A2): (A3, A4, A5) -> R {
return { d, e, f -> this(a, b, c, d, e, f) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6) -> R, R, A0, A1, A2, A3, A4, A5, A6> T.invoke(a: A0, b: A1, c: A2): (A3, A4, A5, A6) -> R {
return { d, e, f, g -> this(a, b, c, d, e, f, g) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7> T.invoke(a: A0, b: A1, c: A2): (A3, A4, A5, A6, A7) -> R {
return { d, e, f, g, h -> this(a, b, c, d, e, f, g, h) }
}
operator fun <T : (A0, A1, A2, A3, A4) -> R, R, A0, A1, A2, A3, A4> T.invoke(a: A0, b: A1, c: A2, d: A3): (A4) -> R {
return { e -> this(a, b, c, d, e) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5) -> R, R, A0, A1, A2, A3, A4, A5> T.invoke(a: A0, b: A1, c: A2, d: A3): (A4, A5) -> R {
return { e, f -> this(a, b, c, d, e, f) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6) -> R, R, A0, A1, A2, A3, A4, A5, A6> T.invoke(a: A0, b: A1, c: A2, d: A3): (A4, A5, A6) -> R {
return { e, f, g -> this(a, b, c, d, e, f, g) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7> T.invoke(a: A0, b: A1, c: A2, d: A3): (A4, A5, A6, A7) -> R {
return { e, f, g, h -> this(a, b, c, d, e, f, g, h) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7, A8) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7, A8> T.invoke(a: A0, b: A1, c: A2, d: A3): (A4, A5, A6, A7, A8) -> R {
return { e, f, g, h, i -> this(a, b, c, d, e, f, g, h, i) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5) -> R, R, A0, A1, A2, A3, A4, A5> T.invoke(a: A0, b: A1, c: A2, d: A3, e: A4): (A5) -> R {
return { f -> this(a, b, c, d, e, f) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6) -> R, R, A0, A1, A2, A3, A4, A5, A6> T.invoke(a: A0, b: A1, c: A2, d: A3, e: A4): (A5, A6) -> R {
return { f, g -> this(a, b, c, d, e, f, g) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7> T.invoke(a: A0, b: A1, c: A2, d: A3, e: A4): (A5, A6, A7) -> R {
return { f, g, h -> this(a, b, c, d, e, f, g, h) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7, A8) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7, A8> T.invoke(a: A0, b: A1, c: A2, d: A3, e: A4): (A5, A6, A7, A8) -> R {
return { f, g, h, i -> this(a, b, c, d, e, f, g, h, i) }
}
operator fun <T : (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9) -> R, R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> T.invoke(a: A0, b: A1, c: A2, d: A3, e: A4): (A5, A6, A7, A8, A9) -> R {
return { f, g, h, i, j -> this(a, b, c, d, e, f, g, h, i, j) }
}
// Run this example: https://pl.kotl.in/LuUAPZxOV
val printFourThings: (Any, Any, Any, Any) -> Unit = { a, b, c, d ->
println("$a $b $c $d")
}
val curried1 = printFourThings(1, 2)
val curried2 = curried1(3)
curried2(4) // Prints: 1 2 3 4
val curried3 = printFourThings("Red")
val curried4 = curried3("Green", "Blue")
curried4("Yellow") // Prints: Red Green Blue Yellow
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment