Closed omermizr closed 3 years ago
Would static assert (i.e. compile time assert) be enough? Or do I miss something about the semantics behind "Generic Constraints"?
Not sure exactly what you mean... The idea is an ability to constrain generic parameters, so you can utilize some preexisting knowledge about the structure of the generic. A derivative example from TS (pseudo syntax):
interface Lengthwise {
length int
}
fn logging_identity<T extends Lengthwise>(arg T) T {
println("$arg.length") // We know it has a .length property, so no error
return arg
}
Full explanation and example: https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints
Oh, after reading the following from the URL you linked, I understood what you mean.
In our loggingIdentity example, we wanted to be able to access the .length property of arg, but the compiler could not prove that every type had a .length property, so it warns us that we can’t make this assumption.
Actually, I never thought that someone could "bend" the semantics generics so much, that you would need to provide an additional functionality (apparently called "generic constraints" in TS and maybe others) to allow the use for which generics were invented.
From my understanding V uses the generics syntax just for compile-time specialization (i.e. something similar to plain macro substitution) and won't do any type checks until the specific use of the generic method/function/routine gets fully specialized. First then types and other stuff will get checked. If my understanding is correct, then "generic constraints" are supported (though without the unnecessary explicit syntax as in TS).
Currently there is still some designing ongoing regarding generics, so @medvednikov will tell us how generics will work in the end.
Generic constraints are useful only if the language supports inheritance, because you can have a chain of types and so, you need to specify (in some cases) a restriction on generic type.
In fact if interfaces was well structured early, the only case for use generic constraint would be <T : int_1, int_2, ..., int_x>
(look Rust traits). Do you really think that it would be used in V? NO, because embedded structs can represent this situation in a clear way. Complicate the syntax is useless in this case.
I'm not yet sure how it will work. I just know I want to keep it as simple as possible. It's very easy to write really unreadable stuff with complex generics options.
Right now it works like C++ templates, so no concepts. Concepts may be useful, but I need to finish interfaces first to see where this goes.
@medvednikov about types, Imho the only things we need are generic structs, embeddable structs and good interfaces (like a more powerful Go). No other syntax.
I tend to agree @LorenzoPirro
I think I might be missing something... We can have multiple structs that implement the same method or have the same properties. And we have interfaces that can represent these structs. Now, let's say I want to write a method that takes a struct that implements some interface and returns the same struct - how can I do that with the current design?
Some code to illustrate:
struct Arr {
length int
}
struct Dict {
length int
}
arr:= Arr{}
dict := Dict{}
new_arr := copy(arr)
new_dict := copy(dict)
new_arr
should be Arr
and new_dict
should be Dict
.
What should be the signature of copy
, assuming I want to restrict the input to structs that have the length
property (and possibly use this property in copy
)?
What should be the signature of
copy
, assuming I want to restrict the input to structs that have thelength
property (and possibly use this property in copy)?
I can see only 2 mutually exclusive use cases:
The body of copy()
will not touch (e.g. read) the length
field of the passed struct and thus I don't see any reason why would you want to constrain it.
The body of copy()
will use (e.g. read) the length
field of the passed struct and thus if the passed struct doesn't provide any, compilation will fail. Why would anyone want to specify anything then? I don't even get the use case of multiple inheritance mentioned above (disregarding V doesn't have inheritance).
I must be missing something really huge as otherwise I absolutely don't get this obsession with the more text written the better the result (I say no, we're not slaves of machines, we're not recreating Java ecosystem nor C++ ecosystem nor any other counter productive system - the machine must figure out what I wanted to say by leveraging a cleverly engineered interface which is the language - not by specifying everything down to the tiniest imaginable irrelevant things manually over and over).
Btw. you can always write some dummy statement like _ := passed_struct.length
in copy()
to transform the case (1) into (2).
Ok, thanks @dumblob for clearing things up a bit! My two cents:
The only thing some people might want is the ability to be explicit, but that doesn't offer more functionality and is more of a philosophical thing. From my perspective, this issue can be closed. Thanks!
Constraints are sum types (not type aliases). Functions
type Foo = int | string
fn name<T Foo>() {}
Methods
type Foo = int | string
fn (x Xyz) name<T Foo>() {}
Errors
struct Bar {}
name<Bar>() // <--- should give an error
name<int>() // <--- should work
type Foo = int
fn name<T Foo>() {} // <--- this should give an error, Foo is not a sum type
@medvednikov it's currently not clear to me if constrains can also be interfaces?
@danieldaeschle Looks good. Interface constraints should be supported as well: https://github.com/vlang/v/issues/6714#issuecomment-722340686
@danieldaeschle Here I propose allowing operators to be defined in a generic interface used for a constraint: https://github.com/vlang/v/issues/6714#issuecomment-772689442
Allowing that might mean that it's not necessary to support sum type constraints.
But it doesn't replace sum types, or?
It's not as precise as a sum type constraint, but I think we need a motivating use case to allow sum type constraints. Why would we want to only allow e.g. builtin integers but not a struct that wraps int and its operators? If there's a reason, is it significant enough to warrant sum type constraints?
@ntrel if you do
if x is MyType
...
else if x is MySecondType
...
else if x is MyThirdType
you can handle different types and limit the possibilities the user can pass. They don't even need to have the same methods or fields.
i think operators constraints should be made as interfaces
interface Plusser {
+
}
or
interface Plusser {
+(it) it // or `any`
}
or
interface Plusser {
+()
}
but the first one seems the cleanest this would work with operator overloading of course
and if you only want builtin ints you can use sumtypes
Some languages support Generic Constraints (C#, Typescript, even Python). This allows you to write generic functions/classes where you have some knowledge about the generic parameter and want to use it.
The D programming language at dlang.org has powerful generic constraints. In D they are called template constraints. They are boolean compile-time expression composed of logical connectives together with __traits
and std.traits
or templates that evaluate compile-time constants (enum NAME = VALUE;
).
I for myself still do not understand why this would be useful in the context of V. E.g. in case of D lang, they mention few "motivational" reasons why they need generic constraints - see https://dlang.org/concepts.html . But reading them sounds like a strawman to me in the context of V lang (all of them have a better and simpler solution in V IMHO - and they are already mostly in place).
So if anyone can come up with some motivating examples as @ntrel wrote above in https://github.com/vlang/v/issues/2711#issuecomment-773455138 ), that would push this forward.
you can handle different types and limit the possibilities the user can pass. They don't even need to have the same methods or fields.
Is someVarable is SomeType
an expression known at compile-time?
But reading them sounds like a strawman to me in the context of V lang (all of them have a better and simpler solution in V IMHO - and they are already mostly in place).
Which are those better solutions?
Is
someVarable is SomeType
an expression known at compile-time?
I think an answer to this has no value in the context of alpha (i.e. pre-1.0) SW (I actually don't know the answer to your question as I don't have time to follow all the quick development of V).
The right question I'd ask instead is whether the type system in V allows this expression to be compile-time - in which case my answer would be yes.
But reading them sounds like a strawman to me in the context of V lang (all of them have a better and simpler solution in V IMHO - and they are already mostly in place).
Which are those better solutions?
Let's take them one by one:
more finely discriminate about which template gets instantiated for given arguments
V has no templates. And with V generics it's fine enough (in a sense it can not be made finer AFAIK).
provides better self-documentation about what characteristics template parameters must have
See (1) - V generics are fine-grained enough. In all other cases it'd be duplication of what's already defined elsewhere. And in case you want to do something against generics, then there is always an assert (useful for app debugging or experimentation with V compiler itself).
can provide better diagnostics when arguments don't match, rather than an obscure error message based on the irrelevant (to the user) internal details of the template implementation
This is a joke - I have yet to see any (class of) error(s) in V which can't be made readable with all the information the compiler already has.
can provide better diagnostics when arguments don't match, rather than an obscure error message based on the irrelevant (to >the user) internal details of the template implementation
This is a joke - I have yet to see any (class of) error(s) in V which can't be made readable with all the information the compiler >already has.
I don't like the current errors based on internal details. If we can make it work I'm fine. But I want something like:
cannot call fun_xyz with type int because ... blah
Some languages support Generic Constraints (C#, Typescript, even Python). This allows you to write generic functions/classes where you have some knowledge about the generic parameter and want to use it. While v doesn't support subclassing, it does have interfaces, which means we can still benefit from this concept. Any chance of this being supported?