#The Type system is a pain... Am I doing this right?

1 messages · Page 1 of 1 (latest)

somber mantle
#

For context, I'm learning Zig alongside Raylib in preparation for making a game engine. Things are mostly going great... Besides painstakingly large amounts of explicit type conversions that I'm running into. Something that should be as simple as:

rl.drawPixel(x, y, 
    rl.Color{ .r = 255, .b = 255, .g = 255, 
        .a = 255 * (1 - std.math.sqrt(x * x + y * y) / max_dist)
    }
);

due to extremely verbose casting, turns into this mess:

rl.drawPixel(@as(u8, @intCast(x)), @as(u8, @intcast(y)), 
  rl.Color{ .r = 255, .g = 255, .b = 255, 
    .a = @as(u8, @intFromFloat(
      255 * (1 - (
        @as(f32, @floatFromInt(
          std.math.sqrt(x * x + y * y)
        )) 
        /
        @as(f32, @floatFromInt(max_dist)))
      )
    )) 
  }
);

Am I really doing this in the intended way? I came to Zig for the ideals of readability and debugging code rather than the language... But this has been the opposite for me.

flat vortex
#

don't need the @as(u8, ...)

#

there is already result location information from the function arguments and from the .a field of Color

#

the alpha part is problematic as you're mixing int and float arithmetic, would be better to do it all in floats then convert at the end

#

for example

const fx: f32 = @floatFromInt(x);
const fy: f32 = @floatFromInt(y);
const fmax: f32 = @floatFromInt(max_dist);
const alpha = 255 * (1 - @sqrt(fx * fx + fy * fy) / fmax);

c.DrawPixel(
    @intCast(x),
    @intCast(y),
    .{ .r = 255, .g = 255, .b = 255, .a = @intFromFloat(alpha) },
    );
somber mantle
#

Thank you, this helps me understand a little bit more about how (the very sparse) type inference works in Zig. I'm also not used to programming in a style friendly to this explicit conversion, so I appreciate the example. I do still find some frustrating lack of type inference even when the Type is completely unambiguous, like what my max_dist is:

    const max_dist: f32 = @sqrt(@as(f32, @floatFromInt(screenWidth * screenWidth + screenHeight * screenHeight)));

the @as and @floatFromInt seem to be unavoidable, even though @sqrt returns the same type as its parameter. The type of the parameter should be able to be unambigouously inferred since max_dist is defined as an f32, right? I figured surely I could at least to @floatFromInt without @as, but that unfortunately did not work.

flat vortex
#

would be good if it worked like that
return type annotation giving result location to the parameter of an anytype function

kind plover
#

@somber mantle remember you are interoperating with C. That's why Ziggified bindings exist, so these things are less awkward

flat vortex
#

last year or two zig has been moving more towards use of result locations and i'm not sure i like it, especially with anytype stuff

#

like lots of things in std.math take anytype instead of the first parameter beign T: type

#

if it were like that you could do

#

const max_dist = std.math.hypot(f32, @floatFromInt(screenWidth), @floatFromInt(screenHeight));

#

no need for @as, the T parameter would give result location to the others

flat vortex
somber mantle
#

^

#

the raylib bindings take i32 for screen width / height (because yes, we need i32 instead of u32 because we're going to have negative resolutions /s), I iterated on those i32s using a range in a for loop and for some reason that will only give you usizes, so I had to convert those to i32s for drawing a pixel, my math has to be in floating point so there's a ton of conversions needed there, and finally the rgba values have to be u8. I love the idea of Zig but these verbose conversions are ridiculous in some cases

#

bit of a rant but I am glad that I now have somewhat decent looking / actually legible code thanks to Matthis' suggestions

#

I'll mark as answered, but if anyone has any other suggestions please do lmk

lament zinc
#

another perspective: when working with raylib, i like to make my own VecN types as an extern struct which matches raylib's layouts. then you can bitcast between them. that way you can make helpers to hide lots of these conversions and keep the conversions in one place so you don't have to struggle with them as much while you're coding.

brittle yew
#

also my advice: always use floats, and use the methods that take floats or vectors

#

the gpu works with floats, so you might as well just use floats

somber mantle
ashen parcel
#

I think it's not that zig is readable in the sense of conciseness or easy to read at a glance

#

that's why languages like Nim exist

#

zig isn't a very expressive language

#

so you can probably get something nice but you can't really come to zig with the mindset of writing functional or something you'd write in Nim and think it will be readable

ashen parcel