vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.88k stars 2.17k forks source link

Simplify Keywords and Syntax where possible #1718

Closed gslicer closed 4 years ago

gslicer commented 5 years ago

I suggest to not pick up lagacy too much and not to use rundandant keywords/aliases. Examples:

Example 1:

byte // alias for u8  
int  // alias for i32  
rune // alias for i32, represents a Unicode code point  

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 to print(''). as "new line" is just another character), it shall be easy to define own printcrlf('') or whatever when needed

Example 3:

mut var := // stuff
var := // stuff

could be defined and clear anough as:

var := // immutable
var = // mutable (you can still let the compiler check that every variable is initialized)

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 modifier const:? please also explain in more detail the difference between const und immutable in the tutorial

Example 6: inconsistent boolean values

nums := [1, 2, 3]
println(1 in nums) // ==> true 

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

gslicer commented 5 years ago

Also , vin instaead of readline and vout instead of println

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 :)

ntrel commented 5 years ago

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.

ntrel commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

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.

gslicer commented 5 years ago

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?

ntrel commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

@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

medvednikov commented 5 years ago

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:

https://golang.org/pkg/time/#Duration

medvednikov commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

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:

https://golang.org/pkg/time/#Duration

It proposes only better reading or did I get something wrong? Good work anyway!

medvednikov commented 5 years ago

It's more readable and safe than using int. And you can define custom methods on Duration, but you can't do it with ints.

medvednikov commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

It's more readable and safe than using int. And you can define custom methods on Duration, but you can't do it with ints.

Then it is not aliasing anymore. It's inheritance/subtyping. Damn you're fast!

gslicer commented 5 years ago

So to sum it up I agree that aliasing is bad, and existing aliases i32/int and byte/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.

avitkauskas commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

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.

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.

ntrel commented 5 years ago

They are distinct types ATM. I filed an issue here about true aliases,

This is #983.

ntrel commented 5 years ago

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>
ntrel commented 5 years ago

aliasing is bad

See this comment about why Go has it: https://github.com/vlang/v/issues/1223#issuecomment-512817560

gslicer commented 5 years ago

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

M4SSD35TRUCT10N commented 5 years ago

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>

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.

ntrel commented 5 years ago

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?

ntrel commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

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.

M4SSD35TRUCT10N commented 5 years ago

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.

gslicer commented 5 years ago

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

medvednikov commented 4 years ago

Primitive aliases were removed from the language.