What features of Odin do you dislike?

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

1 Like

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.

1 Like

The other is that ok and err cant be shadowed

There’s a talk about it over here:

1 Like

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.

4 Likes

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.

6 Likes

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 and this_expanded_case for fields, when the original C is something like thsXpCas. 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.

5 Likes

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.

4 Likes

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.

1 Like

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.

1 Like

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.

5 Likes

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.

5 Likes

First list:

  • We removed using import for very good reasons. I understand you don’t want to have to type math.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 a map, 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.
  • 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 to i64 instead and then doing your signed-based operations than having intptr.
  • 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
3 Likes

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.

3 Likes

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 :slight_smile:

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.

1 Like

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.