diff options
| -rw-r--r-- | build.zig | 3 | ||||
| -rw-r--r-- | src/example.zig | 18 | ||||
| -rw-r--r-- | src/fold.zig | 54 | ||||
| -rw-r--r-- | src/map.zig | 27 | ||||
| -rw-r--r-- | src/newLib.zig | 163 | ||||
| -rw-r--r-- | src/oldLib.zig | 227 | ||||
| -rw-r--r-- | src/root.zig | 4 |
7 files changed, 81 insertions, 415 deletions
@@ -29,6 +29,9 @@ pub fn build(b: *std.Build) !void { b.installArtifact(exe); + const ir_file = b.addInstallFile(exe.getEmittedLlvmIr(), "output.ll"); + b.getInstallStep().dependOn(&ir_file.step); + const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| run_cmd.addArgs(args); diff --git a/src/example.zig b/src/example.zig index cc3d2c8..6622726 100644 --- a/src/example.zig +++ b/src/example.zig @@ -20,6 +20,7 @@ fn addThenMultiply(n: i32, m: i32, q: i32) i32 { pub fn main() !void { var gpa = std.heap.DebugAllocator(.{}){}; + defer _=gpa.deinit(); const allocator = gpa.allocator(); // Currying const curriedAddResult = func.curry(add)(4)(5); @@ -29,16 +30,27 @@ pub fn main() !void { // Map var items = [_]i32{ 0, 1, 2, 3, 4 }; const itemsSlice: []i32 = items[0..items.len]; - const buffer = try func.mapAlloc(allocator, iterThenMul2, itemsSlice); + var buffer: [16]i32 = undefined; + var buffSlice: []i32 = buffer[0..16]; + func.map(iterThenMul2, itemsSlice, &buffSlice); + const bufferAlloc = try func.mapAlloc(allocator, iterThenMul2, itemsSlice); + defer allocator.free(bufferAlloc); std.debug.print("curry(add)(4)(5) = {any}\n", .{ curriedAddResult }); std.debug.print("compose(mul2, iter)(5) = {d}\n", .{ composed }); - std.debug.print("mapAlloc(allocator, compose(mul2, iter), []i32{{ 0, 1, 2, 3, 4 }}) = {{ ", .{}); + std.debug.print("map(compose(mul2, iter), []i32{{ 0, 1, 2, 3, 4 }}, buffer); \nbuffer = {{ ", .{}); // func.map(func: anytype, items: anytype, buffer: anytype) - for(buffer) |item| { + for(buffer[0..itemsSlice.len]) |item| { + std.debug.print("{d}, ", .{item}); + } + std.debug.print("}}\n", .{}); + + std.debug.print("mapAlloc(allocator, compose(mul2, iter), []i32{{ 0, 1, 2, 3, 4 }}) = {{ ", .{}); + for(bufferAlloc) |item| { std.debug.print("{d}, ", .{item}); } std.debug.print("}}\n", .{}); + std.debug.print("foldl(add, 0, bufferAlloc) = {d}\n", .{func.foldl(add, 0, bufferAlloc)}); std.debug.print("I(5) = {any}\n", .{c.I(5)}); std.debug.print("K(5)(7) = {any}\n", .{c.K(5)(7)}); std.debug.print("(S K S K)(5)(7) = {any}\n", .{((c.S(c.K)(c.S)(c.K))(mul2)(func.curry(add)(3)))(7)}); diff --git a/src/fold.zig b/src/fold.zig new file mode 100644 index 0000000..6b910d3 --- /dev/null +++ b/src/fold.zig @@ -0,0 +1,54 @@ +const std = @import("std"); +const typeVerify = @import("util.zig").typeVerify; + +/// ```zig +/// (fn (fn (fn (b, a) b, b, []a) void) +/// ``` +/// Folds a list of items over a function with the accumulator as the first arg +/// Type signature: `(b -> a -> b) -> b -> [a] -> b` +/// `func` is of type `b -> a -> b`, where `items` is of type `[a]` and `accumulator` is of type `b`. +/// Haskell equivalent: `foldl func accumulator items` +pub fn foldl( + comptime func: anytype, + accumulator: (typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[0].type.?), + items: ([]typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[1].type.?), +) typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.? { + var accum_internal = accumulator; + for(items) |i| + accum_internal = func(accum_internal, i); + return accum_internal; +} + +/// ```zig +/// (fn (fn (fn (a, b) b, b, []a) void) +/// ``` +/// Folds a list of items over a function with the accumulator as the second arg +/// Type signature: `(a -> b -> b) -> b -> [a] -> b` +/// `func` is of type `a -> b -> b`, where `items` is of type `[a]` and `accumulator` is of type `b`. +/// Haskell equivalent: `foldr func accumulator items` +pub fn foldr( + comptime func: anytype, + accumulator: (typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[1].type.?), + items: ([]typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[0].type.?), +) typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.? { + var accum_internal = accumulator; + for(items) |i| + accum_internal = func(i, accum_internal); + return accum_internal; +} + +/// Variant of `foldl` where the first element is the base case +pub fn foldl1( + comptime func: anytype, + items: ([]typeVerify(@TypeOf(func), .{.@"fn"}).@"fn".params[0].type.?) +) typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.? { + return foldl(func, items[0], items); +} + +/// Variant of `foldr` where the first element is the base case +pub fn foldr1( + comptime func: anytype, + items: ([]typeVerify(@TypeOf(func), .{.@"fn"}).@"fn".params[0].type.?) +) typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.? { + return foldr(func, items[0], items); +} diff --git a/src/map.zig b/src/map.zig index c532f9e..b04366d 100644 --- a/src/map.zig +++ b/src/map.zig @@ -13,20 +13,9 @@ pub fn map( items: []typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[0].type.?, buffer: *[]typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.?, ) 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 =>{}, - // } + const fInfo =typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn"; + if(fInfo.params.len > 1) + @compileError("Function passed into map must have exactly one argument"); for (items, 0..) |item, i| buffer.*[i] = func(item); } @@ -35,6 +24,7 @@ pub fn map( /// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b) /// ``` /// Map a function onto a list of values, allocating space for the new slice +/// The return value of this function must be freed using `allocator.free()` /// 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]` @@ -42,16 +32,9 @@ pub fn map( pub fn mapAlloc( allocator: std.mem.Allocator, func: anytype, - items: anytype, + items: []typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[0].type.?, ) 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" }); diff --git a/src/newLib.zig b/src/newLib.zig deleted file mode 100644 index fff975a..0000000 --- a/src/newLib.zig +++ /dev/null @@ -1,163 +0,0 @@ -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 diff --git a/src/oldLib.zig b/src/oldLib.zig deleted file mode 100644 index 013daa7..0000000 --- a/src/oldLib.zig +++ /dev/null @@ -1,227 +0,0 @@ -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) blk: { - const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn"; - if (typeInfo.params.len == 1) - break :blk @TypeOf(func); - if (typeInfo.params.len == 2) - break :blk fn(typeInfo.params[0].type.?) - fn(typeInfo.params[1].type.?) typeInfo.return_type.?; - if (typeInfo.params.len == 3) - break :blk fn(typeInfo.params[0].type.?) - fn(typeInfo.params[1].type.?) - fn(typeInfo.params[2].type.?) typeInfo.return_type.?; - -} { - const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn"; - if (typeInfo.params.len == 1) - return func; - if (typeInfo.params.len == 2) - return struct { - fn funct(arg1: typeInfo.params[0].type.?) fn(typeInfo.params[1].type.?) typeInfo.return_type.? { - return struct { - fn func2(arg2: typeInfo.params[1].type.?) typeInfo.return_type.? { - return func(arg1, arg2); - } - }.func2; - } - }.funct; - if (typeInfo.params.len == 3) - return struct { - fn func1(arg1: typeInfo.params[0].type.?) fn(typeInfo.params[1].type.?) fn(typeInfo.params[2].type.?) - typeInfo.return_type.? { - return struct { - fn func2(arg2: typeInfo.params[1].type.?) fn(typeInfo.params[2].type.?) typeInfo.return_type.? { - return struct { - fn func3(arg3: typeInfo.params[2].type.?) typeInfo.return_type.? { - return func(arg1, arg2, arg3); - } - }.func3; - } - }.func2; - } - }.func1; -} - -pub fn curryHelper(func: anytype, args: anytype) blk: { - const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn"; - _=typeVerify(@TypeOf(args), .{ .@"struct" }); - if (typeInfo.params.len == 1) - break :blk @TypeOf(func); - const newInfo = std.builtin.Type{ - .@"fn" = .{ - .calling_convention = typeInfo.calling_convention, - .is_generic = typeInfo.is_generic, - .params = typeInfo.params[1..], - .is_var_args = typeInfo.is_var_args, - .return_type = typeInfo.return_type, - } - }; - _=newInfo; - // break :blk fn(typeInfo.params[args.len].type.?) @Type(newInfo); - break :blk type; -} { - const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn"; - const argInfo = typeVerify(@TypeOf(args), .{ .@"struct" }).@"struct"; - if (args.len == typeInfo.params.len) return struct { - pub fn funcCurry() typeInfo.return_type.? { - return @call(.auto, func, args); - } - }; - const newInfo = std.builtin.Type{ - .@"fn" = .{ - .calling_convention = typeInfo.calling_convention, - .is_generic = typeInfo.is_generic, - .params = typeInfo.params[1..], - .is_var_args = typeInfo.is_var_args, - .return_type = typeInfo.return_type, - } - }; - _=newInfo; - // const newType = @Type(newInfo); - return struct { - pub fn funcCurry(arg: typeInfo.params[0].type.?) type { - var fields: [64]std.builtin.Type.StructField = .{std.builtin.Type.StructField{.name="10",.type=type,.is_comptime=false,.alignment=8,.default_value_ptr=null}} ** 64; - for (argInfo.fields, 0..) |f, i| { - fields[i] = f; - } - var buf2: [3:0]u8 = undefined; - fields[args.len] = .{ - .name = blk: { - break :blk try intToStringZ(args.len, &buf2); - }, - .type = typeInfo.params[argInfo.fields.len].type.?, - .is_comptime = false, - .alignment = @alignOf(typeInfo.params[0].type.?), - .default_value_ptr = null, - }; - const newStruct = std.builtin.Type{ - .@"struct" = .{ - .backing_integer = argInfo.backing_integer, - .decls = argInfo.decls, - .fields = fields[0..args.len+1], - .is_tuple = argInfo.is_tuple, - .layout = argInfo.layout, - } - }; - - // std.debug.print("{any}", .{fields[0..3]}); - const t = @Type(newStruct); - var newArgs: t = undefined; - for (@typeInfo(t).@"struct".fields, 0..) |f, i| { - if (i == args.len) { - @field(newArgs, f.name) = arg; - } else @field(newArgs, f.name) = args[i]; - - } - return curryHelper(func, newArgs); - } - }; -} - -fn intToStringZ(int: u32, buf: []u8) ![:0]u8 { - return try std.fmt.bufPrintZ(buf, "{}", .{int}); -} - -// TODO: Add diff --git a/src/root.zig b/src/root.zig index 721b2a0..20c6a20 100644 --- a/src/root.zig +++ b/src/root.zig @@ -2,4 +2,8 @@ pub const map = @import("map.zig").map; pub const mapAlloc = @import("map.zig").mapAlloc; pub const curry = @import("curry.zig").curry; pub const compose = @import("compose.zig").compose; +pub const foldl = @import("fold.zig").foldl; +pub const foldr = @import("fold.zig").foldr; +pub const foldl1 = @import("fold.zig").foldl1; +pub const foldr1 = @import("fold.zig").foldr1; pub const combinators = @import("combinators.zig"); |
