I tend to agree that something like @Generic(T) that makes the monomorphization unambiguous would be nice. But if it’s going to be a key word - Why not anything?
Just wondering if an item didn’t get migrated from GitHub to codeberg is it still valid or would be considered dropped? Just wondering about that real16,real32,etc proposal?
Still valid, you can read about it near the bottom of https://ziglang.org/news/migrating-from-github-to-codeberg/
Sadly, I think the change as described in the ticket would be a backwards move. Not that I think anytype is a particularly good keyword, but anyval is far worse. Values are not types, and you put this keyword in the slot where you specify a type.
However, I think this should be considered with the more generic concept of type unions, which had a few mentions in the genie thread. Currently there’s just one type union. The union of all types anytype. There’s no way to define any other smaller unions. If there was, we probably wouldn’t need anytype at all.
I don’t know if people are outspoken about the mechanics of any toe, but I personally have enjoyed and used it without much issues. I kinda like the mono morph. and the mechanics of using it. I will say there could very possibly be a better way for maximal control and performance. Just wanted to voice a not dunking side
in zig, types are also values.
a value of type anyvalue, a type value of type anytype. ie, a value of type anytype should be a type. that’s how it sounds to me.
your view that anytype is marking the type slot would make more sense in a language where types and values are in different universes. but we can look that it marks the value, in which case, anyvalue might make more sense.
(i’m hedging because it’s obviously about the personal choice of giving more or less gravity to one or another viewpoint)
I think you’ve misunderstood me a little.
Yes, all types can be used as values, but all values cannot be used as types. To put it more formally. the set of all values contains the set of all types, but the set of all types does not contain the set of all values. I read anytype as the set of all types, and anyval as the set of all values.
You’re right that the two are not in different universes, but they’re not the same either.
To give a concrete example u32 is a type. 7 is a value of that type. I can write fn square(x: u32) saying that x is of the type u32, but I can’t write fn square(x: 7) because “x is of the type 7” is nonsense. As, to me, anyval is a super-set of anytype it includes u32 and it includes 7. fn square(x: anyval) reads as nonsensical to me as fn square(x: 7).
I think for people on the other side of this, they think of anyval as a specific singular type which can take any value. It’s not a singular type though. Each time the function is called it’s resolved down to the specific type it was called with. If anyval was a singular type, the type resolution wouldn’t be necessary.
Edit: After a little more thinking I just wanted to add a more positive contribution.
One thing I don’t like about anytype is just how broad it is. Passing pointer and slices through anytype variables seems bonkers to me, and I don’t think it really has a use case with structured types because that’s why we like tagged unions. I’ve said in the past that having declarable type unions and possibly type variables would be good things. That would allow to say “This argument can be integer or float, it doesn’t really matter” but I understand that that’s a Pandora’s box of language design to have a generic system. Maybe all we need is some predefined type unions for numeric types, and just limit this feature to only that.
any_uintas the set of all unsigned integer typesany_sintas the set of all signed integer typesany_intas the set of all integer typesany_floatas the set of all floating point typesany_numericas the set of all numeric types
and you’ve misunderstood me. I’ll repeat myself – take a pair, its lhs is the value, its rhs is the type. You can look at the anyX either as describing what goes into rhs, or what goes on its lhs. you seem to lean towards it describing the rhs. i say you have a choice
i’m saying, like you, that anyval is a superset of anytype, but in my version, with anytype meaning “lhs is any value of the type type” or “rhs is type”. and the anyval meaning “lhs is any value whatsoever (whose “type” is in keeping with the symbols used in the body of this function)”, or “rhs is not set, it’s to be inferred”
it’s not a type, it’s delegation of type matching to reference of namespace elements instead of type names.
I think for people on the other side of this, they think of
anyvalas a specific singular type which can take any value. It’s not a singular type though. Each time the function is called it’s resolved down to the specific type it was called with. Ifanyvalwas a singular type, the type resolution wouldn’t be necessary.
I think it’s a matter of perspective and I’ll share mine as I fall into this category of people for whom it is a “type” which can take any value. I also share your viewpoint here:
Yes, all types can be used as values, but all values cannot be used as types. To put it more formally. the set of all values contains the set of all types, but the set of all types does not contain the set of all values. I read
anytypeas the set of all types, andanyvalas the set of all values.
So I find it funny that we arrive at different conclusions, but I think the key is in different perspectives, as you also hinted.
When I look at, or write, a function signature I look at it in isolation, without any idea about concrete values that get passed in. This rings especially true if I’m just checking the docs and looking at what functions are available. So, for my understanding it doesn’t matter what it resolves to during compilation. Given that, anyval can truly be anything and I also have to handle that when coding by inspecting with @TypeOf and @typeInfo, as everyone else must. I don’t have prior knowledge of all the function’s callsites.
But I’d also like something a bit more ergonomic on the side of documentation. Some kind of pragma or w/e that can be picked up during docs generation and replace anyval/anytype with the types supported by the function. It doesn’t even need to be a change in the semantics of the language. Private fields in a struct is another example of where I don’t necessarily want changes to the language (i.e. adding actual private fields), just a standardized way to document it and communicate the intent.
For example, it can get annoying having to always look at source code to figure out what are all the types that math functions support. It’s not a deal breaker for me, but it adds friction that would be nice to not have from a user perspective.
I don’t think it’s dunking. In my case it just sounds a bit weird to make sure all imports are explicit, all variables are in scope, but then also going “Oh, this function argument can be anything, don’t worry about it.” Especially considering that interfaces in their most basic classic form are zero runtime cost and help with documentation and tooling. All of which I believe align with the view of what Zig should be as a language.
I agree that the desire to put constraints into function signatures is just and reasonable. However, I believe that the current anytype, supplemented with subsequent comptime type checks, is sufficient for expressing parameter constraints and allows them to be completed within the function signature itself, as referenced in the method mentioned in this post using block expressions. Various solutions such as interfaces seem to me prone to local optima and are not sufficient to express all parameter constraints at comptime.
I agree that the idea of infer is very good, but I think it is equivalent to the current expressiveness of anytype, just a little prettier; if there is infer, that’s great, but if not, I can also accept it.
I think anyval is a stupid thing to rename it to.
I don’t mind the anytype name. I think it clearly communicates that the type of the argument isn’t known in advance, and you have to explicitly request it if you want it.
I don’t really like the infer T idea, because it’s an implicit declaration.
In Zig you expect an identifier to be preceded by var or const, or be an argument to the current function. value: anytype followed by const T = @TypeOf(value); and T: type, value: T don’t violate this rule, but value: infer T and value: @Generic(T) do.
What I wouldn’t mind is the keyword being renamed to simply generic, because that’d actually communicate something the anytype name doesn’t - that having an anytype argument makes a function generic, being recompiled for each unique type passed into it.
I believe communicating this behaviour is one of the reasons why the standard library (usually) prefers the T: type, value: T pattern. When the type itself is an argument to the function, it’s very easy to understand that the function’s behaviour changes based on the passed in type.
I also think it’d be nice to communicate a wholly unique behaviour of anytype - since function arguments normally have a fixed type, whatever you pass into the function is coerced to that type when you call the function. This is not true of anytype, even though it’s true of the T: type, value: T pattern.
But, I doubt there’s any single word that succintly communicates this.
I definitely have needed to clarify to multiple people who are new to the language that the value of something of type anytype is not a type, so anyval is a big improvement.
Obviously I love bikeshedding as much as the next guy, but I think it’s useful to keep in mind the perspective of a newcomer when getting heated about a cosmetic change like this.
Introducing the keyword infer may be better.
I think the logic is inconsistent too from OP’s own logic
Said another way,
A variable that holds some unknown error is an anyerror.
A variable that holds some unknown frame is an anyframe.
A variable that holds some unknown type is a type.
A variable that holds some unknown value is an anytype?
to me an unkown error, is an error, an unkown frame is a frame, an unknown type is a type and an unknown value is a value
If we try to transpose that to real life it will probably make more sense, an unknown glass is just a glass, whether you hand me a beer glass, a wine glass, at the end of the day it’s a bloody glass, never had someone asked me for an anyglass
The more I think about it, the less I understand why anyval should be better than anytype. The content of a variable is always unknown, but the type of that variable is not. Unless it is unknown, then anytype comes into play. And in this case, it is indeed correct, since the type is unknown, hence anytype.
This is not correct. It’s not the variable that is an anytype. The correct phrase should be:
The type of an unknown value is
anytype.
Which is logically correct.
This does not contradict with anyerror. When specifying an error type, we are actually specifying which error set that error belongs to. If the error could belong to any error set, we need an error set that encompasses all errors, and that’s anyerror.
The keyword anytype sits right where a type should be, which is why it makes sense. In any case, this is honestly bike shedding. I guess some people would find one word better than the other, but it would change absolutely nothing. I don’t see how this improves the ergonomics or the ease of learning the language.
I personally don’t think it makes anysense, I mean I understand what you are saying and because this idiom is often used it’s not as weird, but still if you were to refer to all the possible cars you wouldn’t say, “I want to buy an anycar” or “please give me the anysauce” fundamentally an unknown error in an error. to me
pub fn foo(value : infered) error!@TypeOf(value) {
......
}
makes more sense and reads better than
pub fn foo(value : anytype) anyerror!@TypeOf(value) {
......
}
but if we wanted to make it truly consistent i think
it should be.
pub fn foo(value : @infer(type)) @infer(error)!@TypeOf(value) {
......
}
It’s only tangential, but anyerror doesn’t infer the error set. It is a concrete type. It is is the sum of all error sets.
What would be the arguments and return type to this @infer function? If it’s a special syntax, then it shouldn’t look like a function.
Oh yeah I forgot that error set were a global thing. I don’t know I’m trying to suggest something that reads well, I think a builtin reads better, in any case i think anyval is just bad.