diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-21 00:09:07 -0500 |
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-21 00:09:07 -0500 |
| commit | c034b4a1291a803028eef8b0950a7090ea54b047 (patch) | |
| tree | 987040745c887299e69f2c3014c44ea829d89bb3 /src/curry.zig | |
| parent | b4588b2064c3afde9497084caef6e83246b32501 (diff) | |
| download | funcz-c034b4a1291a803028eef8b0950a7090ea54b047.tar.gz | |
Currying, Combinators, and organization
Diffstat (limited to 'src/curry.zig')
| -rw-r--r-- | src/curry.zig | 68 |
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; +} |
