diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-20 03:02:35 -0500 |
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-20 03:02:35 -0500 |
| commit | b4588b2064c3afde9497084caef6e83246b32501 (patch) | |
| tree | 0a25bd30b6f812a2acc9ba35f5c72ba20e9f7844 | |
| parent | 0729305769365c9d29689a52ec37dbd3818a28a4 (diff) | |
| download | funcz-b4588b2064c3afde9497084caef6e83246b32501.tar.gz | |
Added docstrings with proper type signatures
| -rw-r--r-- | src/lib.zig | 97 |
1 files changed, 59 insertions, 38 deletions
diff --git a/src/lib.zig b/src/lib.zig index c84829f..0a50a80 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -1,18 +1,33 @@ const std = @import("std"); +const Type = std.builtin.Type; -/// Compose two single argument functions +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:{ - const outerFuncType = @TypeOf(outerFunc); - const outerFuncTypeInfo = @typeInfo(outerFuncType); - if(outerFuncTypeInfo != .@"fn") - @compileError("Expected function, found " ++ @typeName(outerFuncType)); - const innerFuncType = @TypeOf(innerFunc); - const innerFuncTypeInfo = @typeInfo(innerFuncType); - if(innerFuncTypeInfo != .@"fn") - @compileError("Expected function, found " ++ @typeName(innerFuncType)); + _=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; @@ -26,54 +41,60 @@ pub fn compose( }.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 itemsType = @TypeOf(items); - const itemsTypeInfo = @typeInfo(itemsType); - const funcType = @TypeOf(func); - const funcTypeInfo = @typeInfo(funcType); - if(funcTypeInfo != .@"fn") - @compileError("Expected function, found " ++ @typeName(funcType)); - if(itemsTypeInfo != .array and itemsTypeInfo != .pointer) { - @compileError("Expected array or slice, found " ++ @typeName(itemsType)); - } - switch (itemsTypeInfo) { - .pointer => |p| if(p.size != .many and p.size != .slice) @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)), + 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 []funcTypeInfo.@"fn".return_type.?; + break :blk []funcInfo.@"fn".return_type.?; } { - const funcType = @TypeOf(func); - const funcTypeInfo = @typeInfo(funcType); - var result = try allocator.alloc(funcTypeInfo.@"fn".return_type.?, items.len); + 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 { - const funcType = @TypeOf(func); - const bufferType = @TypeOf(buffer); - const itemsType = @TypeOf(items); - const funcTypeInfo = @typeInfo(funcType); - const bufferTypeInfo = @typeInfo(bufferType); - const itemsTypeInfo = @typeInfo(itemsType); - if(funcTypeInfo != .@"fn") - @compileError("Expected function, found " ++ @typeName(funcType)); - if(itemsTypeInfo != .array and itemsTypeInfo != .pointer) - @compileError("Expected array, found " ++ @typeName(itemsType)); - if(bufferTypeInfo != .array and bufferTypeInfo != .pointer) - @compileError("Expected array, found " ++ @typeName(bufferType)); - switch (itemsTypeInfo) { - .pointer => |p| if(p.size != .many and p.size != .slice) @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"), + _=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| |
