Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify Keywords and Syntax where possible #1718

Closed
gslicer opened this issue Aug 23, 2019 · 27 comments
Closed

Simplify Keywords and Syntax where possible #1718

gslicer opened this issue Aug 23, 2019 · 27 comments
Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.

Comments

@gslicer
Copy link

gslicer commented Aug 23, 2019

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 gslicer added the Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one. label Aug 23, 2019
@gslicer
Copy link
Author

gslicer commented Aug 24, 2019

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
Copy link
Contributor

ntrel commented Aug 26, 2019

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
Copy link
Contributor

ntrel commented Aug 26, 2019

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
Copy link
Contributor

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
Copy link
Author

gslicer commented Aug 28, 2019

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
Copy link
Contributor

ntrel commented Aug 28, 2019

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
Copy link
Contributor

M4SSD35TRUCT10N commented Aug 28, 2019

@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
Copy link
Member

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
Copy link
Member

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
Copy link
Contributor

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
Copy link
Member

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
Copy link
Member

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
Copy link
Contributor

M4SSD35TRUCT10N commented Aug 28, 2019

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
Copy link
Author

gslicer commented Aug 29, 2019

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

ntrel commented Sep 3, 2019

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

This is #983.

@ntrel
Copy link
Contributor

ntrel commented Sep 3, 2019

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
Copy link
Contributor

ntrel commented Sep 3, 2019

aliasing is bad

See this comment about why Go has it:
#1223 (comment)

@gslicer
Copy link
Author

gslicer commented Sep 3, 2019

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
Copy link
Contributor

M4SSD35TRUCT10N commented Sep 3, 2019

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.

@ntrel
Copy link
Contributor

ntrel commented Sep 3, 2019

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
Copy link
Contributor

ntrel commented Sep 3, 2019

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Author

gslicer commented Sep 27, 2019

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
Copy link
Member

Primitive aliases were removed from the language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants