Closed medvednikov closed 2 years ago
The second syntax is easier to understand for me:
fn main() {
mut app := App{}
vweb.run(mut app, port)
}
Second syntax for me as well
I agree with @spytheman and @Delta456 that the second syntax is more intuitive.
I think this is not such a big problem. Second variant is much better for me.
It's fine to call generic functions without the <T>
, I think it's cleaner and it's usual in most languages suporting generics. Being strict is fine, but there are places where it makes sence to give some freedom.
But I have another remark.
What about generic struct
? Can we do that now? Is it needed?
struct Things<T> {
a T
b T
c int
}
small := Things<int>{1, 1, 2}
big := Things<i64>{1, 1, 2}
// Or should it also then be?
small := Things{1, 1, 2}
big := Things{i64(1), i64(1), 2}
Like containers of different types in C++. Perhaps that comes from C++ to me, but I have a feeling that you have to indicate the type of the element when you create a general container, but you never bother specifying the exact type when calling the function (the type is inferred from the parameters). And I like it this way.
@avitkauskas Generic structs are not yet implemented, as you have probably found out by now.
For generic structs, it would be necessary to specify the type to allow partial initialization.
I think if it's a case of either/or, then the first syntax is a lot better. However, I do like the old way of calling vweb.run()... Could we make it optional? Best of both worlds?
The first syntax is the old syntax :) @tobyjwebb
:man_facepalming: Sorry, I meant the other way round... Not enough coffee yet.
@avitkauskas generic structs will be certainly supported. And yes, no way around specifying explicit types:
struct List<T>{}
list := List<int>{}
This issue was opened primarily to discuss generic function call syntax. But we also have to discuss the generics type restriction syntax.
Today this compiles and crashes at run time yet:
fn div<T>(a, b T) T {
return a / b
}
fn main() {
div('aaa', 'bbb')
}
So, we need a syntax to restrict generic types to only certain sets of types, so that compiler could check that and though an error during compilation. Could it be one of these?
fn div<T {int, u32, i64, u64}>(a, b T) T { ... }
fn div<T [i64, u64], U [int]>(a T, b U) T { ... }
or perhaps even better
typeset Numbers {i8, byte, i16, u16, int, u32, i64, u64}
typeset UnsignedNumbers {byte, u16, u32, u64}
// with most usefull typesets predefined somewhere?
fn div<T Numbers, U UnsignedNumbers>(a T, b U) T { ... }
Or was it already thought about and decided?
It doesn't compile for me, I get a C error.
We just need to make V handle it:
error: invalid operands to binary expression ('string' (aka 'struct string') and 'string')
return a / b ;
I think it's fine to just check for every type during compilation without complicating the language with concepts for now.
Today this compiles and crashes at run time yet:
I would treat it as a bug, and try to apply compile time checking on the generic function, and not modify the syntax.
PS: Regarding what I mentioned earlier, about the <> being required or not:
I would make them not required if you can infer the type from the arguments to the function, otherwise they would need to be explicitly defined.
fn foo<T>(f T) { ... }
fn bar<T>(x int) { /* do something with T. NOTE: T not used in parameters */ }
// <> not required to call foo:
foo('hello')
// <> required to call bar:
bar<App>(80)
Oh yes, it really gives C error on compilation, not on run time. My bad. It's OK if it would be checked for every type during the compilation. Though it would restrict us to write generic functions that ONLY work for all types, we could not write then generic functions working only on numbers, or only on signed numbers etc.
I'm not a big proponent of generics until it can be prevented that incompatible types (for the function operations) can be clearly sorted out (I think to ensure this function overloading would be necessary) e.g. adding two integers would call another function then adding (concatenating) two strings or adding two arrays of integers or adding two arrays of strings or... you get my point.
For example what happens if I provide a struct as T arg here:
fn devide<T>(a T, b T) T {
return a/b
}
General question: are generics so beneficial at all that the clearity of syntax can be sacrificed for this matter? Probably V doesn't need its own version of any syntax construct of all other languages out there.
However this could be helpful: Generic Programming
Bunus reading: Advantage of Java Generics it seems like all 3 points mentioned there solve Java problems V does not have, or am I wrong?
For example what happens if I provide a struct as T arg here:
Compilation error. /
cannot be used with this type.
I'm not a fan of generics either, but sometimes they are necessary.
The goal is to keep them small and simple.
As long V detects such conflicts at compile-time it's fine for me, I would prefer the C++ like syntax having those assert T.type() != U.type()
That's a good idea.
Not sure about allowing all letters yet. I like a limited set.
People can get really create with generics, I'd like to limit their usage a bit to achieve simpler code.
Why have assert T != U?
What if the user sometimes wants to pass the same type?
El mié., 23 oct. 2019 11:11, Alexander Medvednikov notifications@github.com escribió:
That's a good idea.
Not sure about allowing all letters yet. I like a limited set.
People can get really create with generics, I'd like to limit their usage a bit to achieve simpler code.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vlang/v/issues/2469?email_source=notifications&email_token=AAE4HSNF6BJ6T336JY77SUDQQAIMDA5CNFSM4JC4T3T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECAVVRQ#issuecomment-545348294, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE4HSIGDBGUMAACM5A24MTQQAIMDANCNFSM4JC4T3TQ .
@tobyjwebb I think this is to improve clarity and readability - when the template creator designed the template to work with same types he should use the same letter for both arguments, don't you think?
What I'm saying is that it's not safe to assume that the T != U:
Say we have a generic function to create a map of type map[T]U
fn create_map<T, U>(k T, v U) {
/// snip
}
What the assert T != U
would imply is that the user couldn't create a
map[string]string
, for example (at least not with this function).
TW
On Wed, 23 Oct 2019 at 11:21, gslicer notifications@github.com wrote:
@tobyjwebb https://github.com/tobyjwebb I think this is to improve clarity and readability - when the template creator designed the template to work with same types he should use the same letter for both arguments, don't you think?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vlang/v/issues/2469?email_source=notifications&email_token=AAE4HSM3P7CPFEYQWGGJ53DQQAJRRA5CNFSM4JC4T3T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECAWXSI#issuecomment-545352649, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE4HSLVMN2SRTXDOVUIRZLQQAJRRANCNFSM4JC4T3TQ .
Why not just use like jai.
func foo(a $T, b T, c int) { °°°}
Why not just use like jai.
func foo(a $T, b T, c int) { °°°}
Interesting, we can simplify it even further, since V types can't be named T, U etc.
fn simple<T>(p T) T {
return p
}
becomes
fn simple(p T) T {
return p
}
@shsr04 what do you think?
This way there are no ugly <>
at all.
@tobyjwebb you are right
What I'm saying is that it's not safe to assume that the T != U:
@medvednikov No what if we have custom type name T We put $T SO the type is exported.
Fun foo(a $T, b $A, c C) A {°°°}
$T and $A not defind types.
C is defind type just like int or whatever.
With $ we can easily handle the erros and tell the compiler and humens for what going stretfoard. Whithout any confusions.
fn simple(p T) T { return p }
@medvednikov I prefer more detailed function declarations. I like to see with one look that a function is generic and which type params it uses. Being short is nice, but I don't see any meaningful advantage in removing the type parameter lists. And someone coming from Java or C++ can easily understand the good old <T, U>
in a function declaration.
What if all one capital letter names [A..Z] in V would be reserved for generic types only and this would be cleary stated in documentation? If you see one letter type, you know its generics. Then you can be short and clear (if you read the docs...) Just thinking...
Yes, that's the plan. One letter types only for generics.
Not sure if we should allow all 26 letters, or a limited set as it's implemented now.
@hamad-almamari
C is defind type just like int or whatever.
In V types can't be one letter long.
@shsr04 I see your point and I agree.
@medvednikov Today this runs, so types can be single letter...
struct C {
a int
}
fn main() {
c := C{10}
println(c)
}
Also, even if structs weren't allowed to be 1 letter long, what about if they were 2 letters? Yesterday I was looking at a bug report where the struct name was 2 letters long: struct St { ... }
fn generic(s St) { } // Easily confused with generics.
For something as "strange" as this, I would push for a more non-confusable syntax (like what is implemented at the moment).
fn do_something<A, B>(...) { } // I like this.
Also, it's the same syntax as in other languages, so it will be an easier transition, and another plus point is that it's already implemented, so it means less work.
On Wed, 23 Oct 2019 at 20:42, Alvydas Vitkauskas notifications@github.com wrote:
@medvednikov https://github.com/medvednikov Today this runs, so types can be single letter...
struct C { a int } fn main() { c := C{10} println(c) }
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vlang/v/issues/2469?email_source=notifications&email_token=AAE4HSJJNFLN5H7LOBSWZLLQQCLITA5CNFSM4JC4T3T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECCOTDA#issuecomment-545581452, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE4HSJ7KPEDPAL6JC6MTOTQQCLITANCNFSM4JC4T3TQ .
I agree with medvednikov, can write one less word is one
@avitkauskas strange, I remember adding this check. I've just pushed a fix.
@tobyjwebb generics are one letter long. Anyway, I decided that current syntax is ok.
Anyway, I decided that current syntax is ok.
Excellent ^_^
On Wed, 23 Oct 2019 at 22:52, Alexander Medvednikov < notifications@github.com> wrote:
@tobyjwebb https://github.com/tobyjwebb generics are one letter long. Anyway, I decided that current syntax is ok.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vlang/v/issues/2469?email_source=notifications&email_token=AAE4HSN6UHRJGSQIRL7SQYLQQC2Q5A5CNFSM4JC4T3T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECC2UXA#issuecomment-545630812, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE4HSMVBCMOXG32Y3TYC6DQQC2Q5ANCNFSM4JC4T3TQ .
I prefer the second syntax
What's about assert T.type() != U.type()
issue, if this is not true, why we need more than one single letter? We always could just use T
or not?
@medvednikov Why cant we do something like this: If we want to have parameter with generic type we can do:
//declaration => call
fn foo(bar T) => foo(8000)
But you can also have additional generic types:
fn foo<T,U>() => foo<FirstStruct, SecondStuct>()
And their combination:
fn foo<T, U>(bar V) => foo<FirstStruct, SecondStuct>(8000)
In this case we won't have to create any useless arguments like we do in our wveb:
//now
mut app := App{}
vweb.run(app, port)
//will be
vweb.run<App>(port)
And this pretty syntax will take it's place
foo(1)
foo('str')
foo(user)
if you declare foo like
fn foo(arg1 T)
It can be possible because we can't have type names in 1 character length
@Danil-Lapirow yes, I agree.
There are situations where we need to pass only types, like in the vweb example.
We can still use <> in declarations to implicitly make generic functions
fn foo<>(bar T)
// it can be still called like
foo(1)
// or
foo('baz')
why don't design the api of vweb like this?
fn main() {
mut app = App{}
app.run()
}
Hello! I just came up to this language a couple hours ago, and was easily hooked by its simplicity!
I really like the idea of delaying to check whether the applied types are valid or not to the use‐site (i.e. when the function is called).
I also really like the idea of dedicating specific names to be type parameters and not declaring them. It’s the same approach as Haskell takes, and it makes the declarations easier to read, I feel like.
I think it’d be neat if it’d be possible to infer types when a function is applied, and require explicit types if that is not the case.
The problem is that if the type parameters are not declared, it’s difficult to know which order to pass the type arguments in when calling the function.
I recently noticed that Go will implement generics - feel free to take a look (it's a complete overhaul of the older official proposal by same authors - I heard this new proposal was made in collaboration with a Haskell developer/architect, but didn't take look who that was).
Maybe V can learn from some of the ideas and mistakes they did (will do).
@medvednikov have you seen this new draft already?
Generics are back thanks to @shsr04.
This time they are more powerful (for example, multiple types are supported).
You can see some examples here: https://github.com/vlang/v/blob/master/vlib/compiler/tests/generic_test.v
One big change is the type parameters being removed from the function call:
becomes
To me it looks a bit like overloaded functions, which are not allowed in V. Of course the function is still the same, and does the same thing, unlike overloaded functions which can do anything depending on the type.
Also some functionality is lost. vweb uses the following construct to build an application object:
All that's needed to set up a web application object:
Now it has to be done with
Which syntax do you prefer?