aboutsummaryrefslogtreecommitdiff
path: root/src/curry.zig
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2025-10-21 00:09:07 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2025-10-21 00:09:07 -0500
commitc034b4a1291a803028eef8b0950a7090ea54b047 (patch)
tree987040745c887299e69f2c3014c44ea829d89bb3 /src/curry.zig
parentb4588b2064c3afde9497084caef6e83246b32501 (diff)
downloadfuncz-c034b4a1291a803028eef8b0950a7090ea54b047.tar.gz
Currying, Combinators, and organization
Diffstat (limited to 'src/curry.zig')
-rw-r--r--src/curry.zig68
1 files changed, 68 insertions, 0 deletions
diff --git a/src/curry.zig b/src/curry.zig
new file mode 100644
index 0000000..d196155
--- /dev/null
+++ b/src/curry.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+
+/// ```zig
+/// (fn (a, b, ..., n) r) fn (a) fn (b) ... fn (n) r
+/// ```
+/// Function currying
+/// Type signature: (a -> b -> ... -> n -> r) -> a -> b -> ... -> n -> r
+/// Transforms a function taking multiple arguments into a sequence of functions each taking a single argument
+/// `func` is a function of type `(a, b, ..., n) -> r`
+/// Haskell equivalent: automatic currying (all functions are curried by default)
+pub fn curry(func: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), .{}) {
+ return curryHelper(func, .{});
+}
+
+
+fn curriedTypeVerify(comptime T: type, comptime expected: std.builtin.Type) std.builtin.Type {
+ const info = @typeInfo(T);
+ if (@as(std.meta.Tag(std.builtin.Type), info) != @as(std.meta.Tag(std.builtin.Type), expected)) {
+ @compileError("Type mismatch");
+ }
+ return info;
+}
+
+fn curryTypeGetter(comptime func: type, comptime newfunc: type, comptime args: anytype) type {
+ const typeInfo = curriedTypeVerify(func, .{ .@"fn" = undefined }).@"fn";
+ const newTypeInfo = curriedTypeVerify(newfunc, .{ .@"fn" = undefined }).@"fn";
+
+ if (typeInfo.params.len == args.len + 1) {
+ return fn(typeInfo.params[args.len].type.?) typeInfo.return_type.?;
+ }
+
+ const nextParamType = typeInfo.params[args.len].type.?;
+ var buf: [64]type = undefined;
+ for (args, 0..) |a, i| {
+ buf[i] = if (@TypeOf(a) != type) @TypeOf(a) else a;
+ }
+ buf[args.len] = nextParamType;
+
+ return fn(nextParamType) curryTypeGetter(func, @Type(.{
+ .@"fn" = .{
+ .calling_convention = newTypeInfo.calling_convention,
+ .is_generic = newTypeInfo.is_generic,
+ .params = newTypeInfo.params[1..],
+ .is_var_args = newTypeInfo.is_var_args,
+ .return_type = newTypeInfo.return_type,
+ }
+ }), buf[0..args.len+1].*);
+}
+
+fn curryHelper(comptime func: anytype, args: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), args) {
+ const typeInfo = curriedTypeVerify(@TypeOf(func), .{ .@"fn" = undefined }).@"fn";
+ const argInfo = @typeInfo(@TypeOf(args)).@"struct";
+ _=argInfo;
+
+ const nextParamType = typeInfo.params[args.len].type.?;
+
+ const Closure = struct {
+ fn funcCurry(arg: nextParamType) @typeInfo(curryTypeGetter(@TypeOf(func), @TypeOf(func), args)).@"fn".return_type.? {
+ if (args.len + 1 == typeInfo.params.len) {
+ return @call(.auto, func, args ++ .{arg});
+ }
+ const newArgs = args ++ .{arg};
+ return curryHelper(func, newArgs);
+ }
+ };
+
+ return Closure.funcCurry;
+}