Thanks I will see how to integrate both projects in Gungnir’s pipeline so maybe it can have the best of both worlds.
if you don’t need history then adding --depth 1
will reduce a lot of that download for history rich repos
I have issues with the shadowing model from two angles:
Odin’s variable declaration syntax is so beautifully succinct its easy to miss a decl vs assignment visually, and I have fat fingers. So I tend to accidentally shadow something and then when scouring the code to see why my data is borked, I struggle to see where I went wrong. :=
is muscle memory, and to be clear its my fault for adding the :
when I didn’t mean to, but the subtlety of it makes it hard to spot often times. I think something about the child scope-only shadowing rules may play into this, but not too sure. Vet Shadowing has been hit and miss in this regard, though I have not tested in some time and LuaWhiperer and I had brought up an oddity with it earlier in the year, so perhaps my complaint is moot, not too sure there.
The other is that ok
and err
cant be shadowed, this is a typing ergonomics thing and I get it, but I still don’t like it. I’m not so sure I’d ask for a change here. I’ve thought about being able to package/globally declare certain identifiers to always be shadow-able, but it seems a bit of a hack. eg something like #always_allow_shadowing(ok, err)
.
My personal wishlist is to have a compiler flag of -no-shadow
and opt in with #shadow
as desired. That might not work too well with the core libraries as they stand if you turned it on though, or would need to at least add that directive to all shadowing usage in core. I think in this way you’d end up with as-is behaviors today but can opt for a more stringent / verbose approach if you have fat fingers like me.
The other is that
ok
anderr
cant be shadowed
There’s a talk about it over here:
I’m a beginner and this is a skill issue. But I kinda wish I can interact with vendor library with more Odin types instead of C types. I’m finding myself doing a lot of cstring, [^]u8, etc. conversions which creates some friction.
There is a time and place to wrap C APIs into a more Odin-friendly user-facing whole, and we do this in core:os
for example.
One reason we’re reluctant to do that for vendor:*
is that plain bindings alone, without the Odin face on them, make it easier for someone to interact with the upstream project’s documentation and examples.
This is something that’s bothered me back and forth any time I consider writing bindings.
- On one hand, I could write them in Odin-style, with
This_Expanded_Case
for structs andthis_expanded_case
for fields, when the original C is something likethsXpCas
. The resulting API has a really good, congruent feel to it, and it’s always easier to understand what the thing is without having to insert all the missing vowels or lookup documentation. - On the other hand, keeping the same-name same-case bindings makes it much easier to reference documentation as you say, but the API feels a little off.
I’m never quite sure which to do sometimes. I’m leaning towards keeping the original C names when writing new bindings, but if it’s possible, someone could also provide an Odin-style/idiomatic library that builds on the C one, like you say for core:os
.
One alternative to this that I came up with while writing the FreeBSD sys
package was to have clear, legible names but have the original name in the comment adjacent to the declaration.
Maybe this binding style issue could be standardized.
For vendor, I’ve tried to keep things matching the original style. I’ve found if you try to convert it, you actually end up in cases where things just don’t work. So the more core Odin style sometimes fails because the original style doesn’t mesh.
For me it’s the context system, at least the allocators. I always set the default allocator to the panic allocator so I know when some API fails to explicitly state that they allocate.
A good example are a few of the procedures in core:log
using context.allocator
despite not taking one in the API.
I very much dislike that slices, dynamic arrays, and maps do not behave like fixed-sized arrays when they are passed into procedures, but rather behave like pointers to fixed-sized arrays.
When i try to assign into a fixed-size array i get a compiler error, which is awesome and I ove having immutable parameters. If I want to assign into an index of a fixed-size array, then I can make the parameter a pointer, which is great.
What i think is not great is when i index into any other builtin container, they dont behave like values (like fixed-sized arrays) but rather like references (like pointers to fixed-sized arrays). When i pass a slice for example into a procedure, it is not possible to know for certain that that procedure will not modify the contents of that slice without looking directly at the source code.
I understand that under the hood container hold a pointer into the address space, other than the fixed-size array, and this is why the indexable space is mutable when passed into a procedure. Still, I can’t help but think that this is a problem and one that can be solved. It would be great to at least opt-in to immutable containers via a parameter tag, for example. Even better if we can specify that a field of a struct (when that field happens to be a reference-like container) can be marked with a tag so that it can be passed into procedures and not have the contents of that field messed with unless the struct is passed by pointer.
That’s because slices and dynamic arrays are literally a struct containing a pointer. So of course they act like pointers. And having this behaviour is EXTREMELY useful too. I rarely pass fixed length arrays as an argument to a procedure and when I do, I don’t need to assign to it in the first place. As for slices, I will assign to their contents all the time, even if the slice itself is immutable.
In short, my code looks like this atm:
ecs.init(ents_cap=100, comp_types_cap=10)
defer ecs.terminate()
ecs.setup_components(Position, cap=100)
ecs.setup_components(AI, cap=100)
using ecs
robot := entity__create()
robot_pos := entity__add(robot, Position)
robot_ai := entity__add(robot, AI)
dummy := entity__create()
entity__add(dummy, Position)
dummy_pos := entity__get(dummy, Position)
I love to put nouns first for procedure names that are conceptual methods. It makes it much easier to reason about them and easier to find them and I know exactly what they are.
I wish I could do something like this in Odin:
ecs.init(ents_cap=100, comp_types_cap=10)
defer ecs.terminate()
ecs.setup_components(Position, cap=100)
ecs.setup_components(AI, cap=100)
using ecs
robot := entity.create()
robot_pos := entity.add(robot, Position)
robot_ai := entity.add(robot, AI)
dummy := entity.create()
entity.add(dummy, Position)
dummy_pos := entity.get(dummy, Position)
Maybe it could be done by allowing one dot to be part of the procedure name “.” or by something like this:
package ecs
Entity :: struct {}
entity :: procspace {
create :: proc() -> ^Entity {}
destroy :: proc(self: ^Entity) {}
add :: proc(self:^Entity, $C: typeid) -> ^C {}
remove :: proc(self:^Entity, $C: typeid) {}
get :: proc(self: ^Enitity, $C: typeid) -> ^C {}
}
So the lack of namespaces in Odin was something I wanted from day one. The code you wrote is effectively not more to type than entity_create
in practice. I understand it might be nice to conceptually group things but it doesn’t really help anyone with the artificial taxonomy in the long run. I’ve seen the problems of this in other languages, I did not want to go anywhere near that. This is why packages are very flat and structs cannot have declarations inside them.
As for ECS, I usually recommend no one uses one unless they are making a AAA game engine that WILL be used by other AAA studios. ECSs are overkill for pretty much everyone.
First list:
- We removed
using import
for very good reasons. I understand you don’t want to have to typemath.Vec3
all the time but why not just aliasing those manually yourself?Vec3 :: math.Vec3
? - Most people don’t believe me but even when Odin has semicolons, it still inserted some in places because people really don’t want to put them everywhere. So to make things as consistent as possible, I did the automatic semicolon insertion rules. If you want to stop this, you can either place the expression inside of parentheses, or use
\
to consume a newline. - No. Odin has
distinct
typing, do the explicit cast. - No. Odin has
distinct
typing, do the explicit slice. - Dynamic arrays only grow with the
append
/resize
/reserve
calls, which you can see precisely how it would interact with a custom allocator. As for amap
, that’s even simpler. Each time it resizes, it creates a new map, copies the contents, and then frees the old allocation. - If you are noticing the inheritance hierarchy problem, that’s because you are adding an extra taxonomy that was not needed in the first place coupled with you not actually thinking about the data properly. I am guessing this high level of hierarchy is for error values, and if that’s the case, try handling them earlier than just making an “omni-error”. If it’s something else, why not try a fat-struct approach or just use enums?
Second list:
- I have considered this before but the point of
bit_set
is to directly map to an integer but with better syntax for sets/flags. - No.
- You can add custom
#+vet
flags to any file you want, along with-vet-packages
. - No. There are very good reasons you cannot decide the types of a
for in
loop. Do the cast manually within the body or write the loop manually. - Please suggest a
slice.size_in_bytes
as an issue/PR on GitHub. - Please suggest turns-based calls for
core:math
as an issue/PR on GitHub.- e.g.
tsin
,tcos
, etc.
- e.g.
- Never going to allow
switch
expressions. Odin has distinct differences between expressions and statements in the language. Ternary expressions in Odin don’t even share the same syntax as a normal if-statement either. Please don’t suggest syntax to me. - I had considered
intptr
before but it’s pretty much always a sign that you are doing something poorly. I recommend converting toi64
instead and then doing your signed-based operations than havingintptr
. - Never going to happen, very bug prone in practice, and just ambiguous whether it was meant to be intended or not.
- You already can:
for a, b := 0, (^mytype)(nil); cond; post {}
- This was previously experimented with many years ago and was removed for good reason: it’s the wrong “abstraction”.
@(rodata)
is what you are looking for
Thank you for your answer Bill,
Just to clarify, I do not want namespaces, basically, I was thinking about another symbol beside “_” to separate a noun from the conceptual method name. Because “_” is already used to separate words.
entity.add
is nicer than
entity_add
to read, because you more clearly see that “entity.add” is probably related to Entity.
Dot looks the best. But could be something else.
I use two underscores: “__” so far
entity__add
to make the difference between “noun” and “method name” and other procedure names. It is uglier than a dot or one underscore but it improves readability to me compared to one underscore.
Just a minor personal thought about this, not pushing anything.
Hi. Thank you for your response.
Like I said in the final paragraph, I understand that slices and dynamic arrays hold pointers. I also never meant to imply that mutating the contents of a slice is not useful, because of course it is useful.
I appreciate the response but I do not feel that it at all addressed any of the criticism I offered, and I did my absolute best to present that criticism as constructively as I possibly could. I’m going to quote the last several sentences of what I posted, because I feel that it got lost in translation somehow.
When i pass a slice for example into a procedure, it is not possible to know for certain that that procedure will not modify the contents of that slice without looking directly at the source code.
I understand that under the hood container hold a pointer into the address space, other than the fixed-size array, and this is why the indexable space is mutable when passed into a procedure. Still, I can’t help but think that this is a problem and one that can be solved. It would be great to at least opt-in to immutable containers via a parameter tag, for example. Even better if we can specify that a field of a struct (when that field happens to be a reference-like container) can be marked with a tag so that it can be passed into procedures and not have the contents of that field messed with unless the struct is passed by pointer.
Thanks again.
This is true. Odin does not have much of a concept of C-style const
immutability, outside of string contents, map keys, and procedure parameters.
Right now, you either have to leverage the type system to do this for you or document which procedures mutate slices. I’d prefer comment documentation myself. Note that the example below doesn’t do anything other than add some friction, but truly, you can already mutate strings too if you really want.
package immslice
import "core:fmt"
Immutable_Slice :: struct($T: typeid) {
raw: []T,
}
wrap_immutable :: #force_inline proc(slice: []$T) -> Immutable_Slice(T) {
return { raw = slice }
}
f :: proc(data: Immutable_Slice(int)) {
for v in data.raw {
fmt.println(v)
}
}
main :: proc() {
a := [?]int{3, 5, 7}
b := wrap_immutable(a[:])
f(b)
}
Thank you for the suggestions
I’m hoping that maintainers of the language make a modification to accommodate the genuine necessity to allow a procedure to inspect a slice or dynamic array without either defensively copying the contents of the container (expensive!) or reading the source-code of the procedure and all the procedures it calls and doing that over again and again.
This is just a “you” thing because I find __
a lot uglier than a single _
, and not really any different to a .
. I understand you are not pushing anything.
I did read your post, so need to quote again, but I think I was not clear enough. @Feoramund explains what I meant very well. What you are asking for is effectively const
qualifiers, which I will not add to Odin.
To get the behaviour you are asking for is fundamentally asking for the viral concept of a const
qualifier.