Closed gslicer closed 4 years ago
Also ,
vin
instaead ofreadline
andvout
instead ofprintln
To be honest I would even prefer generic functions for in-/output where you just need to specify the interface (terminal, file, sockets, etc.) instead of having several functions with different function names :)
val has type i32, got int" when they are just aliases?
They are distinct types ATM. I filed an issue here about true aliases, which Go supports. rune should still be a distinct type, see that issue.
allow creating aliases to types in general
The type
keyword is a general feature, those types are not built-in. Last time I checked it wasn't documented.
the difference between const und immutable
const is a compile time constant, like an enum but without being part of a sequence or namespace.
An immutable variable can be initialised by runtime data, so the compiler doesn't know its value.
I suggest to not pick up lagacy too much and not to use rundandant keywords/aliases. Examples:
[...]
byte // alias for u8 int // alias for i32 rune // alias for i32, represents a Unicode code point
I'm totally new to v but a professional programmer since more than a decade and this is something I absolutely second.
Instead predefining aliases to primitive types (just because you know them from c), allow creating aliases to types in general
V should drop aliases entirely. It would make the language far more consistent to read. I'd like to insist that it would make the language and the compiler even more complicated when you can define your own types not only for rebadging existing ones.
V should drop aliases entirely.
I don't think they do any harm at all when implemented the right way. See the following example which should work:
module main
fn main() {
x := int(5)
y := i32(6)
print(x == y)
}
...but instead gives you: expected type 'int', but got 'i32'
So in summary it seems like the compiler threats them as different types...
An alias shall be just another name to the same type - or even arbitrary name (function, variable, ...) what do you think?
An alias shall be just another name to the same type - or even arbitrary name (function, variable, ...)
+1. This is very useful for generic programming.
@all Please don't feel offended by my strong opinion. Discussions like this help to understand each other.
@gslicer
expected type int, but got i32``
In the current state of the compiler/language the above mentioned example error is wrong compared to what the documentation says. An alias is just another name for the same thing. So I'd expect too that it should compile. But there is absolutely no need to do this (to alias your basic data type) in a completely new language.
@ntrel Aliases have nothing to do with generic programming as per definition they are only new names to something already existent. If you have for example 'just_my_super_special_data_type' this is a lot to type and there it could be nice to just type 'jmssdt' instead (alias to the rescue); but this would be a) a bad decision in the first place when naming the data type (same with functions and alikes), b) your code will become inconsistent over time (believe me, I have seen code your eyes would literally bleed by looking at that) and c) it isn't easy for anyone new to maintain your code as he has to know that 'int' is always 'i32' or any other data type alias (or function and alikes; you name it).
See also the FAQ on vlang.io about macros: "Any plans to implement macros?
No, sorry. Macros can be really useful, but they complicate the code significantly. Every company, team, developer can extend the language, and it's no longer possible to jump into a new codebase and immediately understand what's going on.
V will have sophisticated code generation."
And when 'int' is always 'i32', why is it there in the first place (three alphanumeric characters but one is way more specific [i32])? Recall that v is f#*@ing fast at compiling and I do hope that will stay that quick. If you put aliases in that quotation it'll become as slow as Rust.
IMHO: Generic programming is something where the compiler has to analyze the code more to insert/generate one-the-fly and use the appropriate code when compiling your source.
This seems to match the following quote (shamelessly stolen from Wikipedia and checked in PDF): "Generic programming centers around the idea of abstracting from concrete, efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software." - Musser, David R.; Stepanov, Alexander A., Generic Programming
I don't like having i32 + int and i8 + byte
I'd like to have i32 and i8 removed, because I prefer int
and byte
. But then it was suggested that it'd make the naming inconsistent with i16, i64 etc.
Aliases can be a useful and powerful tool. For example, Go's time.Duration
:
By the way we shouldn't be calling type Foo int
aliasing. It's defining a new type based on the other type. Aliasing is like Go's type A = B
, and V won't have it.
I don't like having i32 + int and i8 + byte
I'd like to have i32 and i8 removed, because I prefer
int
andbyte
. But then it was suggested that it'd make the naming inconsistent with i16, i64 etc.Aliases can be a useful and powerful tool. For example, Go's
time.Duration
:
It proposes only better reading or did I get something wrong? Good work anyway!
It's more readable and safe than using int
. And you can define custom methods on Duration
, but you can't do it with int
s.
So to sum it up I agree that aliasing is bad, and existing aliases i32/int
and byte/u8
should be removed.
Subtyping is different and should stay.
It's more readable and safe than using
int
. And you can define custom methods onDuration
, but you can't do it withint
s.
Then it is not aliasing anymore. It's inheritance/subtyping. Damn you're fast!
So to sum it up I agree that aliasing is bad, and existing aliases
i32/int
andbyte/u8
should be removed.
Possibly the types could made a bit more verbose so there is no need for aliases, like: [u]int(8|16|32|64|128) float(32|64|128)
Of course we do not need "byte", "short", "long", "double" etc. as it is expressed by the numeric width.
i(8|1632|64|128)
u(8|16|32|64|128)
f(32|64|128)
That's perfect, no aliases needed. I love that i32 is always 32 bit and I know that (and with int
I would always want to check back to the manual if it stays the same or different on different platforms).
And if I say type byte u8
, it means byte
is a different type based on u8, but with possible additional methods etc.
i(8|1632|64|128) u(8|16|32|64|128) f(32|64|128) That's perfect, no aliases needed. I love that i32 is always 32 bit and I know that (and with
int
I would always want to check back to the manual if it stays the same or different on different platforms). And if I saytype byte u8
, it meansbyte
is a different type based on u8, but with possible additional methods etc.
As clarified by @medvednikov this is something I did get wrong in the first place. Thus I agree with you @medvednikov and @avitkauskas that subtyping is something v should support, but not doing this with the aforementioned basic data types when there's no need.
I can not repeat it enough: Keep up the good work! I consider to donate on a monthly basis beginning with this years december.
They are distinct types ATM. I filed an issue here about true aliases,
This is #983.
aliasing is bad
there is no need for aliases
Aliases are useful when aliasing a long expression. They are also useful in a compile-time if block to alias to different symbols depending on a compile-time test:
$if windows
{
alias myfoo = win32_foo;
}
else $if linux
{
alias myfoo = linux_foo;
}
@M4SSD35TRUCT10N
Aliases have nothing to do with generic programming
Actually they are useful for aliasing template instantiations:
alias IntTree = btree.BalancedTree<int>
Aliases can even have template parameters themselves:
alias Foo<T> = SomeTemplate<T, T.sizeof>
aliasing is bad
See this comment about why Go has it: https://github.com/vlang/v/issues/1223#issuecomment-512817560
Aliasing is like Go's
type A = B
, and V won't have it.
Absolutely agree, that the Go's notation is not well chosen, would it be possible to have a really clear aliasing using something like this (or similar definition):
alias uLong : u64 // "alias" is a reserved keyword and both names have the same type
fun-fact: there is no keyword starting with "a" yet in V ;)
and you would be able to inspect its type e.g. by:
print(uLong.typeof) // ==> u64
aliasing is bad
there is no need for aliases
Aliases are useful when aliasing a long expression. They are also useful in a compile-time if block to alias to different symbols depending on a compile-time test:
$if windows { alias myfoo = win32_foo; } else $if linux { alias myfoo = linux_foo; }
@M4SSD35TRUCT10N
Aliases have nothing to do with generic programming
Actually they are useful for aliasing template instantiations:
alias IntTree = btree.BalancedTree<int>
Aliases can even have template parameters themselves:
alias Foo<T> = SomeTemplate<T, T.sizeof>
alias myfoo = [...]
This is simply a bad habit. Hard to read in two years (not your example). Verbose code is a gift for those who will get their hands on it later.btree.BalancedTree<int>
way more informative than IntTree
you lost the 'Balanced' and btree
information.Foo<T>
is even more bad because SomeTemplate<T, T.sizeof>
is syntactically totally different than the alias.And of course you'll have to dig deep for every project you're involved in what aliases are set in order to understand the code/library/you name it. One can alias in every single source code holding file to a different name as it is possible to do so. And it will happen that way. So no, to alias is a bad habit. I have currently a project where they "live" that habit for over two decades (luckily only variable names as OpenEdge ABL doesn't support alias of data types) and it is tremendously hard to get through. Think a little longer when naming something. We have 2019(!) and almost any editor or IDE has some sort of autocompletion. Subtyping is something I understand that it is useful because you can add additional methods to the new type as mentioned by @medvednikov , but aliasing is simply renaming an existing thing and one use case of yours shows me that code which uses this will be very hard to understand because you are able to change the syntax. This is a possible source of fault. Gradually changing a code base could be done with different methods. in the time where both aliases are allowed you will find in the wild projects that become a mess because they'll use both types even in one source code file. I have seen this. IMHO.
one use case of yours shows me that code which uses this will be very hard to understand because you are able to change the syntax.
It doesn't change syntax, it simply wraps a template instantiation.
This is a possible source of fault.
How so?
in the time where both aliases are allowed you will find in the wild projects that become a mess because they'll use both types even in one source code file
That's why compilers have a command-line option to make deprecated symbols cause an error.
You are also ignoring the case where the original symbol is private and only the alias name is public. It's an encapsulation tool.
one use case of yours shows me that code which uses this will be very hard to understand because you are able to change the syntax.
It doesn't change syntax, it simply wraps a template instantiation.
This is a possible source of fault.
How so?
Used the wrong word (syntactically) for that - it has a broader meaning in german. What I meant was the following:
Foo<T>
shows me that I can use it with any data type and it will work but SomeTemplate<T, T.sizeof>
will show me that SomeTemplate actually do a sizeof of my type. If I get an error related to that (I can't imagine one specifically) I have to know or search for your alias. But when there's only one template definition and calling I don't have to search for or know about an alias. And all examples where you can simplify things with an alias you should change in the first run when defining. Open an issue in the upstream project - whatever.
in the time where both aliases are allowed you will find in the wild projects that become a mess because they'll use both types even in one source code file
That's why compilers have a command-line option to make deprecated symbols cause an error.
This is exactly what I don't want in this language. Keep it simple stupid. It just adds complexity where no complexity is necessary.
You are also ignoring the case where the original symbol is private and only the alias name is public. It's an encapsulation tool.
Well, actually it then was private for a reason. Now you 'opened the gate'. Encapsulation with aliases? Seriously? It's more an 'override-the-developer-intention-because-i-want-to-do-so' tool. If one want to encapsulate something one make it private for a reason. Mainly because one don't want other objects/developers to use it in 'the wrong way' or urge them to use it via an API.
Well, actually it then was private for a reason. Now you 'opened the gate'. Encapsulation with aliases? Seriously? It's more an 'override-the-developer-intention-because-i-want-to-do-so' tool.
Of course aliases are just another names for the same types, so they would have the same access modifiers as their original types - everything else would add complexity and endanger safty
Primitive aliases were removed from the language.
I suggest to not pick up lagacy too much and not to use rundandant keywords/aliases. Examples:
Example 1:
So, how such an compiler error msg is even possible:
field value #1 'val' has type 'i32', got 'int'
when they are just aliases?Instead predefining aliases to primitive types (just because you know them from c), allow creating aliases to types in general
Example 2: strictly speaking predefined functions like
println('')
are redundant (in this case toprint('')
. as "new line" is just another character), it shall be easy to define ownprintcrlf('')
or whatever when neededExample 3:
could be defined and clear anough as:
Example 4:
for i := 0; i < 10; i++ {}
loops seems also redundant and limiting (e.g. in case you need more than one "running" variable or multiple iterating statements or is it possible to use something like:for i := 0, j:=10, k:='a'; i < 10 && j > 5 || k != 'a'; i++, j-- {}
Example 5: possible inconsistency: why
const ()
is a function and not an access modifierconst:
? please also explain in more detail the difference between const und immutable in the tutorialExample 6: inconsistent boolean values
actual output is ==>
1
If I get more familiar with the V syntax I might propose more of those. It is worth to check out the https://www.ponylang.io/discover/#The-Pony-Philosophy as an good example