aboutsummaryrefslogtreecommitdiff
path: root/src/newLib.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/newLib.zig
parentb4588b2064c3afde9497084caef6e83246b32501 (diff)
downloadfuncz-c034b4a1291a803028eef8b0950a7090ea54b047.tar.gz
Currying, Combinators, and organization
Diffstat (limited to 'src/newLib.zig')
-rw-r--r--src/newLib.zig163
1 files changed, 163 insertions, 0 deletions
diff --git a/src/newLib.zig b/src/newLib.zig
new file mode 100644
index 0000000..fff975a
--- /dev/null
+++ b/src/newLib.zig
@@ -0,0 +1,163 @@
+const std = @import("std");
+const Type = std.builtin.Type;
+
+fn typeVerify(T: type, expected: anytype) Type {
+ const expectedType = @TypeOf(expected);
+ const expectedTypeInfo = @typeInfo(expectedType);
+ if (expectedTypeInfo != .@"struct")
+ @compileError("Expected struct or tuple, found " ++ @typeName(expectedType));
+ const realTypeInfo = @typeInfo(T);
+ for (expected) |e| {
+ if(realTypeInfo == e) return realTypeInfo;
+ }
+ for (expected) |e|
+ @compileError("Expected one of " ++ @tagName(e) ++ ", found " ++ @typeName(T));
+ return realTypeInfo;
+}
+
+/// ```zig
+/// (fn (fn (b) c, fn (a) b) fn (a) c)
+/// ```
+/// Function composition
+/// Type signature: (a -> b) -> (b -> c) -> (a -> c)
+/// `outerFunc` and `innerFunc` are functions of types `b -> c` and `a -> b` respectively
+/// Haskell equivalent: `outerFunc . innerFunc`
+pub fn compose(
+ comptime outerFunc: anytype,
+ comptime innerFunc: anytype
+) blk:{
+ _=typeVerify(@TypeOf(outerFunc), .{ .@"fn" });
+ _=typeVerify(@TypeOf(innerFunc), .{ .@"fn" });
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ break :blk fn(in) out;
+} {
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ return struct {
+ fn func(input: in) out {
+ return outerFunc(innerFunc(input));
+ }
+ }.func;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b)
+/// ```
+/// Map a function onto a list of values, allocating space for the new slice
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]`.
+/// `map` will return a slice of type `[b]`
+/// Haskell equivalent: `map func items`
+pub fn mapAlloc(
+ allocator: std.mem.Allocator,
+ func: anytype,
+ items: anytype,
+) error{OutOfMemory}!blk:{
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .array, .pointer });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)),
+ else =>{},
+ }
+
+ break :blk []funcInfo.@"fn".return_type.?;
+} {
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ var result = try allocator.alloc(funcInfo.@"fn".return_type.?, items.len);
+ for(items, 0..) |item, i|
+ result[i] = func(item);
+ return result;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a, *[]b) void)
+/// ```
+/// Map a function onto a list of values, using a buffer
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]` and `buffer` is a pointer to a value of type `[b]`.
+/// Haskell equivalent: `map func items`
+pub fn map(
+ func: anytype,
+ items: anytype,
+ buffer: anytype,
+) void {
+ _=typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .pointer, .array });
+ const bufferInfo = typeVerify(@TypeOf(buffer), .{ .pointer });
+ const bufferChildInfo = typeVerify(bufferInfo.pointer.child, .{ .pointer, .array });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ switch (bufferChildInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ for (items, 0..) |item, i|
+ buffer.*[i] = func(item);
+}
+
+pub fn curry(func: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), .{}) {
+ return curryHelper(func, .{});
+}
+
+fn curryTypeGetter(comptime func: type, comptime newfunc: type, comptime args: anytype) type {
+ const typeInfo = typeVerify(func, .{ .@"fn" = undefined }).@"fn";
+ const newTypeInfo = typeVerify(newfunc, .{ .@"fn" = undefined }).@"fn";
+
+ // Base case: if we've curried all but one parameter, return final function type
+ if (typeInfo.params.len == args.len + 1) {
+ return fn(typeInfo.params[args.len].type.?) typeInfo.return_type.?;
+ }
+
+ // Recursive case: return function that takes next param and returns curried function
+ 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 = typeVerify(@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) curryTypeGetter(@TypeOf(func), @TypeOf(func), args).ReturnType {
+ // Base case: if we have all arguments, call the function
+ if (args.len + 1 == typeInfo.params.len) {
+ return @call(.auto, func, args ++ .{arg});
+ }
+
+ // Recursive case: create new tuple with additional argument
+ const newArgs = args ++ .{arg};
+ return curryHelper(func, newArgs);
+ }
+ };
+
+ return Closure.funcCurry;
+}
+
+fn intToStringZ(int: u32, buf: []u8) ![:0]u8 {
+ return try std.fmt.bufPrintZ(buf, "{}", .{int});
+}
+
+// TODO: Add tests