const std = @import( "std" ); const print = std.debug.print; pub fn main_0() !void { print( "hello world\n", .{} ); var alpha = Alpha {}; print( "alpha: {d}\n", .{ alpha.a } ); alpha_inc( &alpha ); print( "alpha: {d}\n", .{ alpha.a } ); var beta = Beta {}; print( "beta: {d}\n", .{ beta.b } ); beta_dec( &beta ); print( "beta: {d}\n", .{ beta.b } ); } const Alpha = struct { a :usize = 0, fn inc( this :*Alpha ) void { this.*.a += 1; } }; fn alpha_inc( alpha :anytype ) void { alpha.inc(); print( "here: {d}\n", .{ alpha.a } ); } const Beta = struct { b :isize = 0, fn dec( this :*Beta ) void { this.*.b -= 1; } }; fn has_dec( comptime t :type ) type { // var info :std.builtin.Type = @typeInfo( t ); // how to work with `info`... // TODO review `fmt` code regarding meta logic // https://ziglang.org/documentation/master/std/#src/std/fmt.zig if( false ) { @compileError( "something is wrong" ); } switch( @typeInfo( t ) ) { .@"Struct" => { }, .Pointer => { @compileLog( "hello from struct info", .{ .t = t, .i = @typeInfo( t ) } ); @compileLog( "maybe a Pointer ?" ); }, else => @compileLog( "apparently not a struct..." ), } // return anytype; // bad syntax return t; } fn beta_dec( beta :anytype ) void { return _beta_dec( beta, beta ); } fn _beta_dec( beta :anytype, _ :has_dec( @TypeOf( beta ) ) ) void { beta.dec(); // beta.mul(); } //////////////////////////////////////// // 2024-09-14 fn Monoid( T :type, lambda :fn( *T )void ) type { // return struct { // x :*opaque {}, // fn init( y :*opaque {} ) @This() { return .{ .x = y }; } // fn go( monoid :@This() ) void { lambda( monoid.x ); } // }; return struct { t :*T, // const M = @This(); fn init( u :*T ) @This() { return .{ .t = u }; } fn do( monoid :@This() ) void { lambda( monoid.t ); } }; } fn do_the_thing( comptime T :type, monoid :Monoid(T, // opaque {}, struct { fn y( _:*T )void {} }.y, ) ) void { // @compileLog( struct { fn y( _:*T )void {} }.y ); // @compileLog( @TypeOf( struct { fn y( _:*T )void {} }.y ) ); monoid.do(); } pub fn main_1() !void { print( "hello again\n", .{} ); { // @compileLog( struct { fn y( _:*T )void {} }.y ); // @compileLog( @TypeOf( struct { fn y( _:*T )void {} }.y ) ); @compileLog( Alpha.inc ); @compileLog( @TypeOf( Alpha.inc ) ); } var alpha = Alpha {}; const Alpha_implements_Monoid = comptime Monoid( Alpha, Alpha.inc ); const alpha_monoid = Alpha_implements_Monoid.init( &alpha ); // interface-types.2.zig:92:24: error: expected type 'interface-types.2.Monoid.M', // found 'interface-types.2.Monoid.M' // do_the_thing( Alpha, alpha_monoid ); // ^~~~~~~~~~~~ // interface-types.2.zig:125:24: error: expected type 'interface-types.2.Monoid(interface-types.2.Alpha,(function 'y'))', // found 'interface-types.2.Monoid(interface-types.2.Alpha,(function 'inc'))' // do_the_thing( Alpha, alpha_monoid ); // ^~~~~~~~~~~~ do_the_thing( Alpha, alpha_monoid ); print( "alpha: {d}\n", .{ alpha.a } ); do_the_thing( Alpha_implements_Monoid, alpha ); print( "alpha: {d}\n", .{ alpha.a } ); var beta = Beta {}; const Beta_implements_Monoid = comptime Monoid( beta, Beta.dec ); const beta_monoid = Beta_implements_Monoid.init( &beta ); do_the_thing( Beta, beta_monoid ); print( "beta: {d}\n", .{ beta.b } ); do_the_thing( Beta_implements_Monoid, beta ); print( "beta: {d}\n", .{ beta.b } ); } // 2024-09-15 // okay, creating a type dynamically isn't working.. // ( apparently function signatures are identity based ? ) // lets try simplifying to a literal vtable // even simpler, a single function // that takes a value as a param, and does a thing... pub fn main_2() !void { const do_a_thing = struct { fn do_a_thing( T :type, t :*T, function :fn(*T)void ) void { function( t ); } }.do_a_thing; var alpha = Alpha {}; print( "alpha: {d}\n", .{ alpha.a } ); do_a_thing( Alpha, &alpha, Alpha.inc ); print( "alpha: {d}\n", .{ alpha.a } ); var beta = Beta {}; print( "beta: {d}\n", .{ beta.b } ); do_a_thing( Beta, &beta, Beta.dec ); print( "beta: {d}\n", .{ beta.b } ); } // so a basic templated function works // when passing an instance & a method.. fn UpDn( T :type ) type { // vTable return struct { up :fn(*T)void, dn :fn(*T)void, }; } /// this is an example function /// demonstrating a pseudo-interface in zig /// the "interface" is UpDn, /// the Alpha & Beta types are made to "implement UpDn" /// technically, this works, but is clunky as hell... fn use( T :type, x :*T, // :*opaque {}, comptime vTable :UpDn(T), // :UpDn( @TypeOf(x.*) ), direction :bool, ) void { if( direction ) { vTable.up( x ); } else { vTable.dn( x ); } } pub fn main_3() !void { const Extras = struct { fn a_dn( a :*Alpha ) void { a.*.a -= 1; } fn b_up( b :*Beta ) void { b.*.b += 1; } }; var alpha = Alpha {}; const Alpha_implements_UpDn = .{ .up = Alpha.inc, .dn = Extras.a_dn, }; print( "alpha: {d}\n", .{ alpha.a } ); use( Alpha, &alpha, Alpha_implements_UpDn, true ); print( "alpha (up): {d}\n", .{ alpha.a } ); use( Alpha, &alpha, Alpha_implements_UpDn, false ); print( "alpha (dn): {d}\n", .{ alpha.a } ); var beta = Beta {}; const Beta_implements_UpDn = .{ .up = Beta.dec, .dn = Extras.b_up, }; print( "beta: {d}\n", .{ beta.b } ); use( Beta, &beta, Beta_implements_UpDn, true ); print( "beta (up): {d}\n", .{ beta.b } ); use( Beta, &beta, Beta_implements_UpDn, false ); print( "beta (dn): {d}\n", .{ beta.b } ); } // okay.. now with a type again, // but let's try saving the functions a fields // and maybe the instance as an opaque pointer.. // ( ..also, would the Monoid have worked without init ? ) fn Nomoid( T :type, lambda :fn( *T )void ) type { // return struct { // x :*opaque {}, // fn init( y :*opaque {} ) @This() { return .{ .x = y }; } // fn go( nomoid :@This() ) void { lambda( nomoid.x ); } // }; return struct { t :*T, // do :fn( *T )void = lambda, y :*const fn( *T )void = lambda, // const N = @This(); fn init( u :*T ) @This() { return .{ .t = u }; } fn do( nomoid :@This() ) void { nomoid.y( nomoid.t ); } }; } fn do_another_thing( comptime T :type, nomoid :Nomoid(T, struct { fn y( _:*T )void {} }.y, // @as( *const fn( *T )void, null ), // SEGFAULT, i expect // Alpha.inc, // XXX Beta.dec ; not generic yet.. ) ) void { nomoid.do(); // nomoid.do( nomoid.t ); // XXX not generic imo } pub fn main_4() !void { print( "another hello\n", .{} ); var alpha = Alpha {}; const Alpha_implements_Nomoid = Nomoid( Alpha, Alpha.inc ); const alpha_nomoid = Alpha_implements_Nomoid.init( &alpha ); print( "alpha: {d}\n", .{ alpha.a } ); do_another_thing( Alpha, alpha_nomoid ); print( "alpha: {d}\n", .{ alpha.a } ); var beta = Beta {}; const Beta_implements_Nomoid = Nomoid( Beta, Beta.dec ); const beta_nomoid = Beta_implements_Nomoid.init( &beta ); print( "beta: {d}\n", .{ beta.b } ); do_another_thing( Beta, beta_nomoid ); print( "beta: {d}\n", .{ beta.b } ); } // const Moid = struct { // x :*opaque {}, // lambda :*const fn( *opaque {} ) void, // fn go( moid :@This() ) void { moid.lambda( moid.x ); } // // fn init( y :*opaque {} ) @This() { return .{ .x = y }; } // }; fn Moid( T :type ) type { return struct { x :*T, lambda :*const fn( *T ) void, fn go( moid :@This() ) void { moid.lambda( moid.x ); } fn init( y :*T, action :*const fn(*T)void ) @This() { return .{ .x = y, .lambda = action }; } }; } fn create_moid( // T :type, // t :*T, // vTable :struct { action :*const fn( *T )void }, t :anytype, vTable :struct { action :*const fn( *@TypeOf(t.*) )void }, // ) Moid { // return .{ // .x = @as( *opaque{}, t ), // .lambda = @as( *const fn( *opaque{} )void, vTable.action ), // }; // } ) Moid( @TypeOf(t.*) ) { // return .{ // .x = @as( *opaque{}, t ), // .lambda = @as( *const fn( *opaque{} )void, vTable.action ), // }; return Moid( @TypeOf(t.*) ).init( t, vTable.action ); } fn use_moid( T :type, moid :Moid(T) ) void { moid.go(); } pub fn main_5() !void { print( "hello noir\n", .{} ); var alpha = Alpha {}; const alpha_moid = create_moid( &alpha, .{ .action = Alpha.inc } ); print( "alpha: {d}\n", .{ alpha.a } ); use_moid( Alpha, alpha_moid ); print( "alpha: {d}\n", .{ alpha.a } ); var beta = Beta {}; const beta_moid = create_moid( &beta, .{ .action = Beta.dec } ); print( "beta: {d}\n", .{ beta.b } ); use_moid( Beta, beta_moid ); print( "beta: {d}\n", .{ beta.b } ); } // once more maybe, simplified hopefully // .. // seems that most interfaces are single functions // so basically a generic function.. idk.. // sorting works // `lt` is the *interface*/*trait* // `u8_lt` is *u8_implements_lt* // `isize_gt` is the *isize_implements_lt* // `sort` is the usage of the `lt`-*interface* // note: `lt` coult have been a *vTable* instead of a single fn. // if using a vtable, probably more concise to // make a function `fn(T)vTable` that // takes any input types and returns the vTable type fn sort( T :type, items :[]T, lt :*const fn(T,T)bool ) void { for( 0..items.len ) |i| { var x :usize = i; for( i..items.len ) |j| { if( lt( items[j], items[x] ) ) { x = j; } } const tmp = items[i]; items[i] = items[x]; items[x] = tmp; } } fn u8_lt( a :u8, b :u8 ) bool { return a < b; } fn isize_gt( a :isize, b :isize ) bool { return b < a; } pub fn main_6() !void { // const bytes = "hello world"; // @as(*const [11:0]u8, "hello world") var bytes = "hello world".*; // @as([11:0]u8, "hello world".*) // @compileLog( bytes ); print( "{any}\n", .{ bytes[0..] } ); sort( u8, &bytes, u8_lt ); print( "{any}\n", .{ bytes[0..] } ); var numbers = [_]isize { 2345, 1987, 3049582, 23459, -32345, 435 }; // @as([6]isize, [runtime value]) // @compileLog( numbers ); print( "{any}\n", .{ numbers[0..] } ); sort( isize, numbers[0..], isize_gt ); print( "{any}\n", .{ numbers[0..] } ); } // 2024-09-16 // again, on an interface that has multiple methods: iterators // val() :T // end() :bool // yes, i know; `next() :?T` would be simpler.. proof-of-concept // slice, index // linked list, node fn Iterator_vTable( Iterator :type, T :type ) type { return struct { val :*const fn( Iterator ) T, end :*const fn( Iterator ) bool, inc :*const fn( Iterator ) void, fn next( vt :*const @This(), it :Iterator ) ?T { if( vt.end( it ) ) { return null; } else { defer { vt.inc( it ); } return vt.val( it ); } } }; } fn printEach( Iterator :type, T :type, iterator :Iterator, // comptime vTable :Iterator_vTable( @TypeOf(iterator), T ), comptime vTable :Iterator_vTable( Iterator, T ), ) void { print( "begin\n", .{} ); var i :usize = 0; while( !vTable.end( iterator ) ) { const val = vTable.val( iterator ); print( "{d}: {any}\n", .{ i, val } ); vTable.inc( iterator ); i += 1; } // iterator is modified, "consumed" print( "end\n", .{} ); } fn printChars( Iterator :type, iterator :Iterator, comptime vTable :Iterator_vTable( Iterator, u8 ), ) void { print( "\"", .{} ); while( vTable.next( iterator ) ) |val| { print( "({c})", .{ val } ); } print( "\"\n", .{} ); } pub fn main_7() !void { // var data :[]u8 = "hello"; // var data :[]u8 = "hello".*; // var data :[]u8 = &("hello".*); const str = "hello"; // @compileLog( str ); // const data :[]u8 = str[0..]; // if a slice is desired, // then the source array needs to exist as well. const slice_implements_Iterator = struct { slice :[]const u8, index :usize = 0, fn val( it :*@This() ) u8 { std.debug.assert( 0 <= it.index and it.index < it.slice.len ); return it.slice[ it.index ]; } fn end( it :*@This() ) bool { return it.slice.len <= it.index; } fn inc( it :*@This() ) void { std.debug.assert( 0 <= it.index and it.index < it.slice.len ); it.index += 1; } }; var data_iterator :slice_implements_Iterator = .{ .slice = str[0..] }; printEach( *slice_implements_Iterator, u8, &data_iterator, .{ .val = slice_implements_Iterator.val, .end = slice_implements_Iterator.end, .inc = slice_implements_Iterator.inc, } ); // var di2 = .{ .slice = data }; // @compileLog( di2 ); // @as(struct{slice: []u8}, [runtime value]) var di2 :slice_implements_Iterator = .{ .slice = str[0..] }; // @compileLog( di2 ); // @as(interface-types.2.main.slice_implements_Iterator, [runtime value]) printChars( *slice_implements_Iterator, &di2, .{ .val = slice_implements_Iterator.val, .end = slice_implements_Iterator.end, .inc = slice_implements_Iterator.inc, } ); // NOTE passing PointerTypes & vtables into type variables // can have surprising side-effects; // mutating values... // var root :?*Node = &.{ .val = 'b', .next = // &.{ .val = 'y', .next = // &.{ .val = 'e', .next = null } // } // }; var node_e :Node = .{ .val = 'e', .next = null }; var node_y :Node = .{ .val = 'y', .next = &node_e }; var node_b :Node = .{ .val = 'b', .next = &node_y }; var root :?*Node = &node_b; const iterator_vTable :Iterator_vTable( *?*Node, u8 ) = .{ .end = Node__end, .val = Node__val, .inc = node_inc, }; printEach( *?*Node, u8, &root, iterator_vTable ); var r2 :?*Node = &node_b; printChars( *?*Node, &r2, iterator_vTable ); print( "fin\n", .{} ); } const Node = struct { val :u8, next :?*@This() }; // fn node_push( handle :*?*Node, ... ) *Node { new_node.val = param; new_node.next = handle.*; } // fn node_pop( handle :*?*Node ) u8 { ... } fn node_end( node :?*const Node ) bool { return node == null; } fn node_val( node :*const Node ) u8 { return node.val; } fn node_inc( handle :*?*const Node ) void { handle.* = if( handle.* ) |here| here.next else null; } fn Node__end( handle :*?*const Node ) bool { return node_end( handle.* ); } fn Node__val( handle :*?*const Node ) u8 { return node_val( handle.*.? ); } // i'm content with the iterator demo of zig-interfaces // ... // grand finale pub fn main() !void { // print( "main_0:\n", .{} ); // try main_0(); // print( "main_1:\n", .{} ); // try main_1(); print( "main_2:\n", .{} ); try main_2(); print( "main_3:\n", .{} ); try main_3(); // print( "main_4:\n", .{} ); // try main_4(); print( "main_5:\n", .{} ); try main_5(); print( "main_6:\n", .{} ); try main_6(); print( "main_7:\n", .{} ); try main_7(); }