<adamkowalski>
passing by value means it's implementation defined (maybe it fits in a register or it's actually faster to pass by reference) but it means you will not mutate
<adamkowalski>
that should be the default
<adamkowalski>
passing by pointer means I will mutate this
<adamkowalski>
passing by const pointer means I will not mutate this, and I want to garauntee it's passed by pointer
<Kingsquee>
I guess that's fair
<Kingsquee>
basically the same behaviour as C, but with an additional optimization
<adamkowalski>
yeah don't think about the optimization. just think pass by value signifies no mutation
<adamkowalski>
I actually think we should flip the notation to be honest
<adamkowalski>
I wish both pass by T and *T were immutable
<Kingsquee>
well, not quite, since passing by const pointer also signifies no mutation
<adamkowalski>
and *mutable T
<Kingsquee>
*var T
<adamkowalski>
yeah I wish var got renamed to mutable so it's long and ugly and people avoid it. and you have to pay the ugly tax if you want to use a mutable variable
<Kingsquee>
being from rust, I'm not a fan of the ugly tax
<adamkowalski>
right now var is short than const which is no fair
<Kingsquee>
mutable might not be so bad, but with regards to how they handle unsafety, it sounds nice but in practice you need to do a lot of unchecked get/sets after your parameters are sanity checked
<Kingsquee>
so the ugly is concentrated in <literally every hot code path>
<adamkowalski>
not sure I follow
<Kingsquee>
well, with gamedev you're doing tons of container gets/sets
<adamkowalski>
that sounds like a particular design
<Kingsquee>
and in rust the idiomatic approach is to have everything be bounds checked
<adamkowalski>
when you say gets/sets are you just talking about accessing things inside of an array?
<Kingsquee>
or some custom container, yes
<adamkowalski>
i'm not sure I agree, I think all writes should be visible and obvious and yelling out to you
<adamkowalski>
shared mutable state is the biggest source of bugs in most programs
<adamkowalski>
it's a huge performance pitfal too since it means everything requires syncronization
<adamkowalski>
however, yes sometimes i'm guessing in your domain you might have positions or something you want to update
<adamkowalski>
I would argue that you still want that broadcasted to the world so when a reader sees it they know "here be dragons
<Kingsquee>
the approach I use is have handles representing whatever object I'm looking up, and getters/setters for each field of object
<adamkowalski>
and shouldn't those getters setters have wildly different properties?
<Kingsquee>
so something like `let health = item_db.get_health(item_handle).expect("Could not get health");`
<adamkowalski>
getters being mostly fine, don't require syncronization (maybe a reader lock) and very little coordination
<Kingsquee>
most of the time, the expect is only called if item_handle is invalid
<Kingsquee>
so in practice asserting item_handle is valid and then calling unchecked getters from there-on is desirable
<Kingsquee>
but in practice, rust makes your eyes bleed when you do that and calls your mother names
daex has quit [Ping timeout: 260 seconds]
daex has joined #zig
<Kingsquee>
if only their borrow checker treated structs and arrays as many individual variables rather than a single variable :(
<Kingsquee>
life would be easy
<pmwhite>
Yeah, if only P=NP.
daex has quit [Ping timeout: 240 seconds]
daex has joined #zig
<adamkowalski>
Kingsquee well there are issues with that approach
<adamkowalski>
if you access a single element in an array you bring in a whole cache line right
<Kingsquee>
pmwhite: its fine, throw machine learning at it
<adamkowalski>
so if I access that element, and you access the next element
<Kingsquee>
yup
<adamkowalski>
it's just as bad as if we are writing to same variable
FireFox317 has quit [Ping timeout: 260 seconds]
<adamkowalski>
but I suppose in a single threaded context it could work
<adamkowalski>
just give each element it's own lifetime and ownership model
<Kingsquee>
you're referring to the point of accessing individual values rather than a cache-line's worth of them?
<Kingsquee>
I've thought of this, and I don't see a way to get around it in practice
<adamkowalski>
right
<adamkowalski>
x[0] = 5 on thread one
<adamkowalski>
x[1] = 10 on thread two
<adamkowalski>
Thats why my strategy is to partition the array into sub sections whose length is a multiple of cache lines
<Kingsquee>
thing is, if you use a custom container, you can have the get/set queries be per-object, but internally have the data be tightly packed in an array
<adamkowalski>
then you can distribute the segments between threads
<Kingsquee>
so the frontend is a sparse lookup table while the back is cache-friendly
<adamkowalski>
within each thread you process the segmend in a serial fasion using simd
<adamkowalski>
also try not to operate on individual objects if possible, that's my main issue with getters/setters
<adamkowalski>
try to operate on the whole pack at once
<adamkowalski>
then the branch predictor is on your side
<Kingsquee>
so for functions where you are just ripping through the same operation applied to some fields of these objects, can just work directly on the internal array
<adamkowalski>
and the prefetcher
<Kingsquee>
but when you need to access individual objects, go through the getter/setter indirection
<adamkowalski>
you're going to take a cache miss but yes
<Kingsquee>
the assumption being the functions that operate on the field arrays are going to be hotter and more involved than the ones which don't
<Kingsquee>
and the ones which don't..I don't know how to improve it
daex has quit [Ping timeout: 258 seconds]
<Kingsquee>
maybe some kind of intelligent sorting of the whole pack, which might end up being slower if there aren't enough queries to justify it
<adamkowalski>
I wouldn't sort
<adamkowalski>
I would enter them into the system in a sorted fashion in the first place
<adamkowalski>
that way you can avoid the if down the road, the data is already grouped together in such a way that it should be accessed together
<Kingsquee>
oo true
darithorn has quit [Quit: Leaving]
<Kingsquee>
things change each frame though, so the sorting needs to be kept up
<Kingsquee>
reminds me, one guy did a partial sort each frame
<Kingsquee>
so if not much changed it converged to optimality
<Kingsquee>
if lots did, things were usually slightly better
daex has joined #zig
daex has quit [Ping timeout: 256 seconds]
daex has joined #zig
daex has quit [Ping timeout: 240 seconds]
daex has joined #zig
daex has quit [Ping timeout: 256 seconds]
daex has joined #zig
slurpie has joined #zig
daex has quit [Ping timeout: 256 seconds]
daex has joined #zig
waleee-cl has quit [Quit: Connection closed for inactivity]
daex_ has joined #zig
daex has quit [Ping timeout: 256 seconds]
adamkowalski has quit [Ping timeout: 255 seconds]
adamkowalski has joined #zig
ky0ko has joined #zig
daex_ has quit [Ping timeout: 240 seconds]
daex has joined #zig
<pixelherodev>
There a way to have an anon union accessed as part of a struct? e.g. `struct {union{a:,b:}}.a`?
<pixelherodev>
Actually, that was a poorly designed data structure; redesigned to not need that
daex has quit [Ping timeout: 258 seconds]
daex has joined #zig
ky0ko has quit [Remote host closed the connection]
daex has quit [Ping timeout: 255 seconds]
daex has joined #zig
adamkowalski has quit [Quit: Lost terminal]
daex_ has joined #zig
daex has quit [Ping timeout: 255 seconds]
slurpie has quit [Read error: Connection reset by peer]
daurnimator has quit [Ping timeout: 240 seconds]
daex_ has quit [Ping timeout: 240 seconds]
daex has joined #zig
daurnimator has joined #zig
<pixelherodev>
... huh. andrewrk, Zig's comptime implementation actually gives me a good idea on how to implement an optimization I was doing on LLVM IR earlier :)
<pixelherodev>
Basically going to copy Zig's model
dddddd has quit [Ping timeout: 240 seconds]
daex has quit [Ping timeout: 268 seconds]
daex has joined #zig
_whitelogger has joined #zig
ur5us has joined #zig
_Vi has joined #zig
_whitelogger has joined #zig
_whitelogger has joined #zig
ur5us has quit [Ping timeout: 240 seconds]
FireFox317 has joined #zig
TheLemonMan has joined #zig
FireFox317 has quit [Remote host closed the connection]
jjido has joined #zig
_Vi has quit [Ping timeout: 240 seconds]
jjido has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
tdc has joined #zig
jjido has joined #zig
Kingsquee has quit [Quit: Konversation terminated!]
tdc_ has joined #zig
tdc has quit [Ping timeout: 258 seconds]
jjido has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
_whitelogger has joined #zig
_Vi has joined #zig
r4pr0n has joined #zig
r4pr0n has quit [Quit: r4pr0n]
mahmudov has quit [Ping timeout: 255 seconds]
dddddd has joined #zig
tdog has joined #zig
tdc_ has quit [Ping timeout: 255 seconds]
mahmudov has joined #zig
waleee-cl has joined #zig
marijnfs has joined #zig
Akuli has joined #zig
jessermeyer has joined #zig
<jessermeyer>
Is there a way to query for the maximum positive value represented by an integer type?
<fengb>
std.math.max(usize)
<jessermeyer>
<3
<jessermeyer>
Errr, that's just the max of two values.
<jessermeyer>
pub fn max(x: var, y: var) @TypeOf(x, y) {
<TheLemonMan>
std.max.maxInt
<TheLemonMan>
s/max/math/
<fengb>
Oops yeah what LemonMan said >_>
<jessermeyer>
<3
<TheLemonMan>
andrewrk, yo, do you still have the latest glibc ready and built?
frett27 has joined #zig
Patrice_ has joined #zig
<mq32>
has anyone tried building a c++ standard library with zig build yet? :D
<mq32>
would be cool to remove that dependency on the system tree
<jessermeyer>
Is the intention to @enumToInt() to support bitwise operations for C enums used as bitflags?
<mq32>
i think its more for serialization
<mq32>
bitflags in zig are done with packed strucs
<jessermeyer>
I'm dealing with a C API with extern enums.
<mq32>
hm, you can do bit operations with @enumToInt and @intToEnum
<mq32>
but note that your enum must be non-exhaustive
<mq32>
which are extern enums by default i think
<jessermeyer>
That's what I was doing -- so that's how Zig intends for that to occur?
<jessermeyer>
Yeah, translate-c is importing them as non-exhaustive.
<andrewrk>
TheLemonMan, yes I do
marijnfs has quit [Ping timeout: 256 seconds]
<TheLemonMan>
andrewrk, can you also pull stuff from `dl.abilist` ?
<TheLemonMan>
oh wait, I see you've uploaded the `update_glibc.zig` source too
<andrewrk>
it takes an entire day and 74 GiB of disk space to obtain a build of glibc for every target
<fengb>
Testing that on CI would be out of the question? 🙃
<pixelherodev>
*wow*
<shakesoda>
that sounds an awful lot like a massive engineering fail in glibc
<TheLemonMan>
no way I'm rebuilding it
<andrewrk>
I can send you just the abilist files if you like
<andrewrk>
actually you can get those simply from a source checket
<andrewrk>
*checkout
marijnfs has joined #zig
marijnfs has quit [Client Quit]
gchristensen has quit [*.net *.split]
rom1504 has quit [*.net *.split]
shachaf has quit [*.net *.split]
xvilka has quit [*.net *.split]
shachaf_ has joined #zig
gchristensen has joined #zig
rom1504 has joined #zig
AndroidKitKat has quit [Remote host closed the connection]
rjtobin has joined #zig
bsrd has joined #zig
danyspin97 has joined #zig
AndroidKitKat has joined #zig
shachaf_ is now known as shachaf
dom96 has quit [*.net *.split]
cota has quit [*.net *.split]
cota has joined #zig
dom96 has joined #zig
AndroidKitKat has quit [Remote host closed the connection]
AndroidKitKat has joined #zig
AndroidKitKat has quit [Quit: バイバイ〜!]
jjido has joined #zig
adamkowalski has joined #zig
AndroidKitKat has joined #zig
Cogitri has joined #zig
Ichorio has joined #zig
AndroidKitKat has quit [Quit: バイバイ〜!]
jessermeyer has quit [Remote host closed the connection]
rjtobin has quit [Ping timeout: 255 seconds]
Yardanico has quit [*.net *.split]
tines9 has quit [*.net *.split]
flokli has quit [*.net *.split]
tines9 has joined #zig
Yardanico has joined #zig
ur5us has joined #zig
flokli has joined #zig
<danyspin97>
Hi there!
<mikdusan>
allo
<danyspin97>
I have a little codebase written in Dlang that I would like to convert to zig
<danyspin97>
however it makes heavy use of OOP
<mq32>
hey danyspin97
<danyspin97>
how is the state of OOP in zig? I read the issue #130 and it is seems like it is planned but I'd like to ask if there are more details
<mq32>
OOP as in "inheritance" is not planned for Zig
<mq32>
right now, there are three interface patterns
<andrewrk>
only? I was only aware of 2
<andrewrk>
*orly
<mq32>
the old one from Allocator, the new one (pretty similar, but still different)
<mq32>
we have pure duck typing
<mq32>
and we have alexnask approach
<mq32>
danyspin97: what features of OOP do you need?
<danyspin97>
sadly, I use inheritance a lot in that codebase
<adamkowalski>
danysppin97: is there a reason it needs to be done in a OOP style? It might be worth thinking about the problem you are trying to solve and then ask what the best tool for the job is
<mq32>
yep
<mq32>
you can "work around" inheritance a lot
<adamkowalski>
if you are just using inheritence it is probably quite straigh forward to use composition instead
<mq32>
i converted three c++ projects of mine to zig, one pending
<mq32>
and some made use of inheritance, now they don't use it anymore
<adamkowalski>
if you want runtime polymorphism then you can start to ask about using some sort of vtable abstraction
<adamkowalski>
of which my personal favorite is type erasure
<adamkowalski>
but just using structs for grouping together data is well supported in zig
<danyspin97>
I guess inheritance can be simulated with some effort
<mq32>
if you have fixed number of classes, you can use tagged unions
<adamkowalski>
well what is your application about, it sounds like we have a hammer so we are looking for nails
<adamkowalski>
what is your problem statement
<danyspin97>
mq32: yea, I have a fixed number of classes
<adamkowalski>
do you know which will be chosen at compile time or only at runtime
<danyspin97>
I am writing a rc manager that uses s6 library under the hood
<adamkowalski>
compile time known -> static polymorphism
<adamkowalski>
runtime known and you have a closed set of structs -> use a union
<danyspin97>
it takes service definitions from files and then change its internal status based on the action the user requested
<adamkowalski>
runtime known and you need to be able to extend the set of possible types -> vtable + dynamic dispatch
<adamkowalski>
perfer them in the order listed there as each step down has worse performance
<danyspin97>
I don't know which type is a service until I parse the file
<adamkowalski>
how many possible types do you have
<danyspin97>
but there are only 3 types
<adamkowalski>
oh thats a perfect candidate for a union
Yardanico has joined #zig
<danyspin97>
this number is fixed
<adamkowalski>
even if it changes you will have that freedom, it just can't change dynamically while the program is running
<danyspin97>
yea, which is exactly what I need
<danyspin97>
nice to know it can be done
<adamkowalski>
however, you might be able to do better
<adamkowalski>
why do you need a single type which can represent different types of objects
<adamkowalski>
can you have three arrays, each array stores one type of object
<adamkowalski>
now you will have contiguous layout in memory
<adamkowalski>
as you parse the file you store the appropriate service in the appropriate array
<danyspin97>
that's a great idea!
<adamkowalski>
no polymorphism needed, great cache utilization, great performance
<danyspin97>
does zig supports interfaces i.e. different structs that must implement function x and y?
<mq32>
zig nativly supports duck typing
<mq32>
so you can pass stuff to a function that implements an interface
<mq32>
if you need that at runtime, you need a more complex solution
<danyspin97>
nice, thanks for the answer and the help!
<adamkowalski>
well it's only a little more complex, I can show you the code in my library to do that. Or I recommend reading the standard library allocators, they implement that pattern
<adamkowalski>
but you can do much better then just checking interfaces in Zig
<adamkowalski>
you have compile time execution
<adamkowalski>
you can write a function which accepts a type as a parameter and queries arbitrary things about it, such as what methods it supports or anything you want
<adamkowalski>
then you can write your own custom error messages, so you can check things that have much more semantic meaning in your domain then just do you implement this interface
Patrice_ has quit [Read error: Connection reset by peer]
frett27 has quit [Read error: Connection reset by peer]
<danyspin97>
you can write a function which accepts a type as a parameter and queries arbitrary things about it < Really good for studying things
<danyspin97>
I guess I'll package it for exherbo and try the language features
<danyspin97>
Oh, and thanks for this: "we continue to maintain the C++ zig implementation enough to the point that it can build the latest self-hosted compiler."
<danyspin97>
after having headaches for days using bootstrap setup of other languages, reading this is a relief
AndroidKitKat has joined #zig
jjido has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
AndroidKitKat has quit [Quit: バイバイ〜!]
Akuli has quit [Quit: Leaving]
tdog has quit [Ping timeout: 240 seconds]
AndroidKitKat has joined #zig
AndroidKitKat has quit [Client Quit]
jjido has joined #zig
AndroidKitKat has joined #zig
<shakesoda>
anyone know what type i'd use for an array like { "foo", "bar", null }
<shakesoda>
(i've got a c interface that expects this, and I'm not sure how to deal with it)
<fengb>
Possibly `[*:null]?[*:0]const u8`
<shakesoda>
fengb: that seems to work if I use [_:null]
<shakesoda>
although now i need to get this into [*c][*c]const u8
<shakesoda>
oh, got it
<shakesoda>
just had something as const that needed to be var
<shakesoda>
fengb: thanks
<shakesoda>
aw, it crashes
AndroidKitKat has quit [Quit: バイバイ〜!]
AndroidKitKat has joined #zig
<shakesoda>
I'm going to just adjust the API itself since I have that power this time, but I'd still like to know how to address this in the future
<TheLemonMan>
what's the expected C type?
<shakesoda>
[*c][*c]const u8
<TheLemonMan>
you need some ptrCast
<pixelherodev>
null should implicitly cast to `[*c]const u8`...
<TheLemonMan>
I've explained the very same problem a few days ago, implicit casts only affect the outermost type
<pixelherodev>
Thanks!
<TheLemonMan>
found the ticket, #4596
<pixelherodev>
"Safety-checked illegal behavior" Ah, trying to beat C in the most important war of all: the war of *language!*
ur5us has quit [Ping timeout: 260 seconds]
<pixelherodev>
(I'm sorry)
<pixelherodev>
I couldn't resist
adamkowalski has quit [Quit: Lost terminal]
<fengb>
You’d be surprised how negatively people react to UB
<shakesoda>
figured out my real problem here, I solved this array problem a little while ago already
<shakesoda>
my string just isn't null terminated.
<mq32>
yeah because they don't understand why UB is necessary
<shakesoda>
i.e. a different one being passed into the same function
<mq32>
it's not nice, but if you don't define UB, you don't have a performant language
<shakesoda>
curse you, null terminators
<fengb>
I think it’s more that in C, it’s literally “anything goes”
<fengb>
Whereas we check most of it so it’s not really undefined in safe modes
<mq32>
yeah, this as well
<mq32>
i think it's good to have "runtime-checked illegal behaviour" vs. "uncheckable illegal behaviour"
<mq32>
last thing is just impossible without huge overhead
<shakesoda>
if I add a null terminator to a string myself, how can i convince zig that my []u8 is now a [:0]u8
AndroidKitKat has quit [Quit: バイバイ〜!]
<shakesoda>
take a new slice or something?
<fengb>
You can manually slice it with `foo[0..len:0]`
<adamkowalski>
I'm making a library for tessor algebra and gradient based optimization
darithorn has joined #zig
<adamkowalski>
I'm aiming for something which is easy to read coming from numpy/tensorflow/pytorch
TheLemonMan has quit [Quit: "It's now safe to turn off your computer."]
<fengb>
Might want to checkout std.testing.allocator. It'll do some rudimentary leak counting for you
<fengb>
The code looks fine but I don't understand any of it probably because I don't have any ML experience
<adamkowalski>
thanks for the tip about the testing allocator, I guess I should switch to it all for my unit tests. It might not report to much because the graph currently uses an arena to back all it's allocations so it will just be freed at the end of scope
<adamkowalski>
regarding the code, is there anything in particular you can recommend as far as it being hard to understand?
<adamkowalski>
I would love to know what to focus on for the documentation, and or how to change the API to make it more intuitive
<fengb>
It's just a lot of verbiage I don't understand. sigmoid, gradient
<fengb>
I'm probably the wrong audience :P
<adamkowalski>
ah okay, well just some super brief context
AndroidKitKat has quit [Quit: バイバイ〜!]
<adamkowalski>
The goal is to able to create and manipulate matrices, vectors, scalars, or even high dimensional tensors
<adamkowalski>
so "a" is a 2x2 matrix
<adamkowalski>
sigmoid is defined towards the top of the file, in terms of more primitive operations such as adding and dividing
AndroidKitKat has joined #zig
<adamkowalski>
gradient lets you take the partial derivative of any tensor with respect to any number of tensors
<adamkowalski>
means take the partial derivative of c with respect to a
<adamkowalski>
in order to take derivatives of arbitrary expressions I need to know the whole computation you are trying to perform
<adamkowalski>
hence the library 'compute graph'
<adamkowalski>
you record all your operations on the graph, then feed them to the session to actually run them
daex has quit [Ping timeout: 255 seconds]
<jjido>
why do you need "try" before every function?
AndroidKitKat has quit [Quit: バイバイ〜!]
daex has joined #zig
<shakesoda>
jjido: only ones that can error
<jjido>
setting up the arena allocator uses many lines of code... and why do Graph and Session use allocator instead of arena.allocator? I find it confusing
adamkowalski has quit [Ping timeout: 246 seconds]
AndroidKitKat has joined #zig
<jjido>
ok, "constant" can error out?
<shakesoda>
you're right, there is a *lot* of trying in this
* shakesoda
just clicked
daex_ has joined #zig
daex has quit [Ping timeout: 250 seconds]
<jjido>
The notation &[_]Tensor{y} is not particularly shocking, but I'd recommend explaining why it is needed for the users. (the [_])
AndroidKitKat has quit [Quit: バイバイ〜!]
daex_ has quit [Ping timeout: 246 seconds]
AndroidKitKat has joined #zig
* jjido
realise adamkowalski (~adamkowal@50-39-103-70.bvtn.or.frontiernet.net) left IRC
<fengb>
I’ve been doing a lot of parsing so my try-to-lines ratio is about 1.2 >_>
adamkowalski has joined #zig
<adamkowalski>
jjido: thanks for all the points
<adamkowalski>
the reason you need try before every function is due to two reasons
<adamkowalski>
first you are adding the operation to the graph, this means the graph may need to grow in size, hence you may run out of memory and will fail
<adamkowalski>
secondly, I do shape checking and type checking, so if you try to add to matrices and they have different shapes you will get a shape mismatch and will fail
THFKA4 has joined #zig
<adamkowalski>
jjido: the graph and session both internally create an arena anyway and will manage their own memory, the reason why I create an arena is because in other examples I create many constants (which are allocated on the heap)
<adamkowalski>
this way I don't have to clean up all the constants that are created, and instead they will all be cleaned up when the arena goes out of scope due to the defer
<jjido>
can you do no-ops on a failed graph and report an error only when you try to run it?
<adamkowalski>
good question, if you perform an operation on the graph and it fails, the graph was not mutated and so it is still in a valid state
<adamkowalski>
I try to give you error messages as soon as possible though because you would much rather get an error when adding two matrices then later when calling session.run
<adamkowalski>
however, session.run still does error checking as well because you may have loading a graph in dynamically rather than creating it yourself using the syntax shown
<jjido>
wait, are the matrices being added before running anything?
<adamkowalski>
nope
<adamkowalski>
it's completely lazy
<adamkowalski>
it will do no work untill session.run
<jjido>
then my suggestion would work.
<adamkowalski>
this means I have the full program available to me and can act as a compiler and optimize it
<adamkowalski>
i'm confused by your suggestion I guess
<jjido>
get rid of all the "try" except on run, just store a status variable saying there was an error
<adamkowalski>
can you elaborate
<adamkowalski>
well you can't do that
<adamkowalski>
adding operations to a graph allocates memory
<mikdusan>
if everything is lobbed into a session, maybe it can be the context? and provide arena? or do you want some components to outlive session
<adamkowalski>
for two reasons, the nodes on the graph are polymorphic, and you don't know the operations until runtime
<jjido>
that's a failure mode
<adamkowalski>
so you ned to allocate the operation itself
<adamkowalski>
you also need to allocate space on the graph to store the operation (in practice it's an array list but you shouldn't know that)
<adamkowalski>
either of those steps can fail
<adamkowalski>
and also as I mentioned, I still infer the shapes and propagate that information down even though no addition has happened yet
<jjido>
For example: "const b = try sigmoid(&graph, a);"
<adamkowalski>
precisely because I want to fail early and get an error message on the line where the error occured
<adamkowalski>
rather then every error occuring at session.run
<jjido>
ok fine, that's a design choice
<adamkowalski>
in theory if you were able to construct the graph, it should not crash when calling session.run
<adamkowalski>
baring running out of memory
<adamkowalski>
in fact if you can do your graph construction in a comptime block then those runtime errors turn into compile time errors
<adamkowalski>
and you can get more garuantees about the safety of your program
<jjido>
your choice is to fail in any graph construction step, if it was me I would fail only on run
<adamkowalski>
you can't only fail on run for the reason I already mentioned
<jjido>
I see
<adamkowalski>
adding any operation to the graph requires memory allcoation
<adamkowalski>
the graph is a growing data structure
<adamkowalski>
so you would need to try even if no shape checking was happening
<jjido>
and? if it can't be allocated, then the error was in adding the step
<adamkowalski>
right but why keep going if you have a failed graph?
<jjido>
just to avoid having "try" everywhere
<adamkowalski>
Yeah I understand that, and originally tried to avoid it
daex has joined #zig
<adamkowalski>
but then decided that this is better, because I can unify compile time and run time checking
<jjido>
cool
<adamkowalski>
and debugging would be a nightmare otherwise
<adamkowalski>
if I just say that adding failed because of a shape mismatch
<adamkowalski>
and you have multiple adds in the graph
<adamkowalski>
how do you know which one?
<adamkowalski>
zigs error handling doesn't let you attach data to the error union
<adamkowalski>
it must be error.ShapeMismatch or at best error.AddShapeMistmatch
<jjido>
you can store the data elsewhere, that's not really an issue
<jjido>
anyway you have good reasons for that design
<jjido>
so it's just a question of getting used to it
<adamkowalski>
and I'm not against changing it, I welcome the feedback and just wanted to present my thought process about how I arrived there
<adamkowalski>
I encourage you to keep attacking it and questioning every decision I made
<adamkowalski>
otherwise I won't end up with the best design
<jjido>
I think yor point about compile-time graph construction is valid
<adamkowalski>
I believe you also had a concen about the &[_]Tensor{y} syntax
<adamkowalski>
the rational for that is that when you call session.run you may want the value of several nodes in the graph
<jjido>
Yeah [_] looks out of place coming from non-zig languages
<adamkowalski>
so you can give me a list of tensors to compute, and I find the minimum set of nodes in the graph needed to find their values
<adamkowalski>
if a node is not necessary to find the answer it will never be computed
<adamkowalski>
the [_] means infer the length of the array
<adamkowalski>
then & will cast it to a slice of []const Tensor
<adamkowalski>
however, if you think just being able to run session.run(.{y}, .{}) is better I could make that work
<jjido>
no I think the current one is ok
<adamkowalski>
i could accept a var, look at the type info if it's a array then do what I'm doing right now, otherwise if it's an anonymous list then collect all the members and do something with it
<adamkowalski>
but if it's fine for now I have other big ticket items left on the list
<jjido>
So, why not use "Graph.init(arena.allocator)"?
<jjido>
in your code
<adamkowalski>
well let's back up for a second
<adamkowalski>
the graph uses an arena internally already, it will wrap whatever allocator you pass in
<jjido>
is it necessary?
<adamkowalski>
the reason is all the nodes in the graph must live the same amount of time as the graph itself
<adamkowalski>
so the nodes of the graph have the same lifetime as the graph, hence it will use an arena and when you get rid of the graph you get rid of all the nodes in one go
<adamkowalski>
keep in mind the graph does not actually store tensors (besides constants which get allocated on the heap using the graphs arena and make a copy of the value being passed in)
<adamkowalski>
the session is what actually does the work and can use a differnet allocator then the graph, but will also use a arena internally wrapping the passed allocator
<adamkowalski>
the session and graph have disjoint lifetimes
<adamkowalski>
you may run the session many times, and each time it will allocate all the memory it needs to give you the answer, then blow everything away when it's done
<adamkowalski>
we also down below in the example have the expected value which is defined with the eager.constant