What features of Odin do you dislike?

I very much agree with this one.

Another approach for naming functions of modules could be similar to how SDL names its functions, where instead of:

module_do_something()

you would write the module in all caps as:

MODULE_do_something()

Not the prettiest either but it’s much better for readability than an ambiguous name that does not immediately and clearly separates the action from the object.

For me personally, I usually like organizing my code by modules where each module that is large enough to warrant its own folder-subfolder structure (say for example a renderer module that handles Vulkan on multiple platforms or an ECS module) has its own folder-subfolder structure.
In such cases simply prefixing the function names with MODULE_ doesn’t fully address this, as Odin expects you to put all source files flat in the same folder / package.

I could make it into a package of course, but unless I go out of my way to make it entirely project agnostic to warrant being considered a standalone library (as cyclical dependencies are forbidden), it’s not worth the risk of finding out there are more dependencies than I’ve initially thought and having to refactor a bunch of code.

Maybe that’s just a sign of me being a bad developer though, who knows. :^)

I wonder if a mix of collections for file organization and something like the OBJECT_action(); naming convention are a potential answer to this, though I could be very off-base :sweat_smile:

I am still very much new with Odin though so take my opinion with a grain of salt*

I would like it if I didn’t need to manually type the type of a parametric argument when calling a parametric proc.

For example in my codebase I have the proc:

window_eval :: proc(win: ^Window, function: Eval_Function($T), args: T, callback: Eval_Callback) { ... }

When calling it must specify the type of args, even though it can be safely inferred from the call:

// must:
webapp.window_eval(win, func, Resolve_Args{id, value}, cb)
Resolve_Args{id, value}, cb)

// want:
webapp.window_eval(win, func, {id, value}, cb)

This a technical problem with how parametric polymorphism works, and it’s probably never going to be “fixed”. Part of the reason for never “fixing” this is to keep the type inference system sane and fast. If I was wanting to solve this in the general case, I can see edge cases which would become O(N!) to find the best fit, and I do not want to make the same mistakes as Swift.

2 Likes

Not really a dislike, more an improvement suggestion.

I wish Odin implemented multi cases matches switch like in Swift.

IMHO it’s a good way to lower the cognitive load in the code. It really helps clean the code and understand the logic instead of stacking if / switch statements everywhere.

Odin is never getting “pattern matching”, which that is effectively one step from.

TLS and x509 certs :wink:

Also, OLS is a mess, somebody please fix the dang Zed extension’s weird interaction with odinfmt causing the cursor to go to the end of the file, and as for features I would like… green thread/coroutine library? And also an alternative to Golang’s timer/ticker.

Literally nothing in that list is related to Odin - the language - but libraries and tooling…

See, I see it as: the standard library and tooling is just as much a part of Odin as anything else, and it’s one of the most important parts of Odin that is getting neglected a little, imo.

Imo, the standard library and its semantics are intertwined with the language grammar, and one of the best places to see this is with the context and allocators (and the entire runtime package). The context is both part of the grammar and part of the standard library. The core/standard library also affects how people express programs, and the Odin ecosystem has a particular set of pragmatics, how identifiers are written, and a set of idioms.

The question was “what features of Odin do you dislike?”. The question does not specify the language grammar specifically. Therefore, imo, it includes the standard library and the compiler and the cli interface. This does include odin’s check command, which is part of the cli tool and presumably relies on the parsing of the compiler, and is literally relied on by OLS.

But let me just rephrase it: OLS being maintained outside the Odin project is probably not sustainable. It’s a mess and it needs to be fixed by those who are experienced in Odin’s grammar and parsing.

I understand gingerBill’s distaste for language servers, but he’s offered no better alternative, so this is the reality we live in. It offers no value to anyone to complain about things that you will never choose to fix, especially when you have the knowledge and capabilities to do so.

Most everything else people have suggested about the language has already been considered by the team, and the responses above pretty much show that. The language is already excellent anyways. I wanted to focus on more important matters: the standard libraries and the tooling.

I apologize. What triggered the harsh(er) response to your complaint is asking somebody else to fix an extension in a random text editor that has absolutely no relationship to the Odin project.

OLS is, as you said, not part of the project. And the knowledge on the compiler/standard lib not necessarily overlaps with how a good LSP supposed to work, so just making something official and part of the project ain’t gonna fix it.
Human resources will, whether it’s a separate project or not, so you are welcome to contribute.

I also don’t think it’s Bill’s responsibility to tell anyone what to use instead.


Regarding the specific stdlib related stuff:

TLS is tricky, openssl exists and there’s very little hoops to go through to use it from Odin even right now. I believe I saw ready-made bindings on GitHub as well.

Parsing certs is afaik on the menu, but the base crypto algos are being worked on first. Help is always welcome.

Green threading is probably a not going to happen, not because it’s impossible but because it requires writing pretty much a second standard library to have locks, sockets, I/o primitives all yield instead of blocking by default. Usually language runtimes and stdlibs are designed around green threads if that’s the primary way of concurrency…

Timer/ticker in go piggybacks on green threads. You can mimick them with thread+Chan, or just an if statement with a check on elapsed time… It really depends on how your program is written, and because of that I don’t see how it would be generally useful in the stdlib.

Green threads cannot be just added as a library feature and require it to be a language-level thing. And green threads are not the same as coroutines. Green threads to work well require a heavy runtime with automatic memory management.

As for the rest, those are all just libraries, and thus not related to Odin the language itself.

1 Like

I understand gingerBill’s distaste for language servers, but he’s offered no better alternative,

Then you have literally not understood what I have said.

My point is that I found that they made me less productive for me. So the “better alternative” was to not use them. And that’s just me and I am not saying anything else about an LSP/intellisense-like behaviour.

So for me, a “better alternative” is a nonsensical statement to make.

New thing I don’t like in Odin…
I can do the following

resourceCounts: [max(u16)]uint,

But I cannot do it for bit_fields, which are just integers to begin with:

Resource :: bit_field u16 {
    type: ResourceType | 8,
    subtype: ResourceSubType | 8,
}

//...
resourceCounts: [max(Resource)]uint,

Resource is a u16, so I guess I don’t see how this shouldn’t be allowed if I want to be able to iterate over all possible values of a Resource, which is the whole reason I made it a bit_field to begin with.

Anyways, this stuff was mostly an experiment, so maybe I’m doing some very wrong things here, but I feel like I’m not. Bit_fields are integers that contain within them fields at the bit level. They should be treated like integers because that’s what they are data-wise, and then when I need the fields, I should be able to get them.

Just for more context, ResourceType is an enum, and ResourceSubType is a u8 that I then define the enums PlantType and TreeType as (which I can cast to and from based on the main type when dealing with the Resource; not exactly ideal, but it’s something that kinda-ish works). I do it this way because I use PlantType and TreeType enums by themselves and I use them within this Resource Type, so it lets me basically combine two separate enums into one ordered integer type (which isn’t an enum, but it’s closer than a struct would be).

And I’m going to get a little crazy here, but hear me out: ordinal integers are just like enums, but where the numeric value is also the constant (1 the constant is 1 the value). Which leads me to believe that perhaps, just maybe, something like this should work just like they work with enums :smiley:

resourceCounts: [u16]uint,

Resource is not a u16, it’s a bit_field that is backed by u16. So asking to do max on it makes literally no sense. You cannot use any of the comparison operators on it so asking for max makes no sense either.

As for [u16]uint, that’s never happening because it’s asking for trouble. It has two possible interpretations:

  • a fixed length array of length max(u16)
  • a slice that only takes u16 indices

In either case, that is not worth it as a concept. Enumerated arrays with enums makes a lot more sense in practice.

2 Likes

stackless coroutines are essentially a compiler transformation and defining the abi around them. (and it’s fairly straightforward to make them C++ compatible). Though it affect some guarantees around defer

stackfull coroutines (green threads) don’t necessarily require deeper integration IME. Though a runtime setup to deal with them fully isn’t a light one.

Not sure if it’s the same as what @CristianDG wrote before but I think it’s similar.
Take the case of wanting to export one Odin package as a dynamic library, and then importing it in another Odin package.

In C/C++ you have the API macro trick which you prefix to functions you want either exported or imported in the header file.

#ifdef BUILD_DLL
  #define API __declspec(dllexport)
#else
  #define API __declspec(dllimport)
#endif

void API someFunction(int x, int y); 

In Odin you don’t have header files – which is great – but that means you have to either foreign import everything or use core:dynlib, both requiring you to write your definitions twice - once in the exporting package and once in the importing package. Thrice if you want type safety on the procedures.

pseudo code example:

package exporting_package

init_proc_type :: #type proc(some_var: SomeStruct) -> Result // 1st

@(export)
init : init_proc_type : proc(some_var: SomeStruct) -> Result { // 2nd
  // do something
}
package importing_package

import "core:dynlib"

Symbols: :: struct {
  init: init_proc_type // 3rd
}

// load symbols with dynlib.initialize_symbols()..

This kind of feels like using header files but with an additional step…

With libraries built from other languages I don’t see another way around this, but since it’s an Odin package it makes me wonder if there is a way to make this more straight forward.

Either way, this is the only real difficulty I have when using the language as it makes hot reloading quite cumbersome and error prone, but that aside, I really enjoy writing in Odin.

1 Like

I don’t really like symbols being public by default in packages. I think being private to package is a better default.

That sounds like a good idea until you realize it isn’t and you actually want something to be public which was made private by default without much thought.

1 Like

You can apply a default visibility on all exported values of a file: Overview | Odin Programming Language

Using #+private before the package declaration will automatically add @(private) to everything in that file:

#+private
package foo

And #+private file will be equivalent to automatically adding @(private="file") to each declaration. This means that to remove the private-to-file association, you must apply a private-to-package attribute @(private) to the declaration.

1 Like

No way to disambiguate between builtin proc groups and package defined ones has been a pain point for me. If I’m creating a package which has “append” as a proc group, I’m giving up the ability to use “append” inside that package (at least through the proc group).

My workaround has been using “_append” to distinguish from the builtin one but it’s kinda awkward from the library user perspective. If I could use the defined package name within the package (like “foo.append” inside “package foo”), that would be nice.

It’s only a minor nitpick though, with the obvious workaround being to not use the builtin proc groups when writing a package. I wonder what the drawback would be of such a feature though.

You can also do this to access builtin things which have been shadowed:

import "base:builtin"

builtin.append(...)
1 Like