aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2025-10-20 03:02:35 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2025-10-20 03:02:35 -0500
commitb4588b2064c3afde9497084caef6e83246b32501 (patch)
tree0a25bd30b6f812a2acc9ba35f5c72ba20e9f7844
parent0729305769365c9d29689a52ec37dbd3818a28a4 (diff)
downloadfuncz-b4588b2064c3afde9497084caef6e83246b32501.tar.gz
Added docstrings with proper type signatures
-rw-r--r--src/lib.zig97
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|