Git Product home page Git Product logo

Comments (122)

ducdetronquito avatar ducdetronquito commented on May 18, 2024 43

Hi @medvednikov !

My answer will be off-topic: I just wanted to thank you for what you are doing with Volt and V, both look very promising !

I see a huge amount of feature requests, and I just wanted to say: Take your time, good softwares takes time. It's easy to get overwhelmed building Free softwares: sometimes it's better to say "no" or "not for now" in order to build great things in the long run :)

Have a good day !

from v.

burlesona avatar burlesona commented on May 18, 2024 19

Just curious, why not go the other way with strings, and only use double quotes?

That seems more cross language consistent as it means you get variable interpolation etc., and in my experience apostrophes are much more common in strong literals than double quotes, so if you’re going to pick one the double quotes will require less escaping.

from v.

xorhash avatar xorhash commented on May 18, 2024 9

Here's an assortment of thoughts:

From a Ruby and C background, I quite appreciate constants having to be uppercase. It also helps grepability (you know that you have to grep 'const CONSTANT_HERE' -r * if you want to find the value of a constant).

You may already want to put special thought into the HTTP library since I assume it will see a lot of usage. In particular, you may want to support both synchronous and asynchronous patterns; consider some standard library way to event loop all kinds of networking.

I enjoy the design of the or keyword and how you can immediately force a return. However, I'm not sure if an implicit $err is always the right solution. I'm worried about localization and internationalization of error messages in particular.

How do you return failure from main? This isn't as much of a concern on Windows and for GUI applications, but your example doesn't seem to address a case like int main(void){return EXIT_FAILURE;} in C. I'm also rather happy about eprintln() existing; you may, however, want to look at the BSD err(3) family, which print a message to stderr and then exit with the given exit status.

I'm not sold on having both ' and " as string delimiters with no semantic change. I know that Python does that, but considering UNIX Shell, Perl and Ruby all consider ' to be special in prohibiting variable expansion, you might want to do the same.

Looking at the printing examples: Is it accurate that a variable identifier can be any character.

What happens if you try to println() an array type? My intuition would suggest that a compilation error would have the least surprise, but you may want to do something else.

What even is an int? Is that a literal C int with all the risks of undefined behavior on signed overflow? Are unsigned types even available? Sun's experience with Spring (the OS from the 90s, not today's Java framework) showed that you kind of need unsigned integers for writing device drivers.

Are you making any guarantees about the C code output of a function that only does arithmetic on integer types and has no branching? This may be relevant for the implementation of cryptographic algorithms in particular.

Concurrency remains an open issue to address in another example, too, probably.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 5

Hi @loic-sharma

One of the main philosophies of V is to make code readable, maintainable, and predictable.

One of the requirements for this is having only one way of doing things. Every single feature of the language has only one right way. The exception right now is string literals (see the ' vs " discussion).

If uniform function call syntax is implemented, every single function can be called in two ways. This is unacceptable, sorry.

V is a very strict language, and the one way only rule is going to make about half of programmers not happy by definition. C++, Nim, Rust, Scala etc are languages that support lots of paradigms and give developers lots of freedom. As the result, the complexity increases, code can get hard to understand, maintainability suffers.

Go has a similar philosophy. And I remember lots of people not happy about being forced to place { on the same line and using tabs instead of spaces. But in the end I think most will agree that it was the right decision that saved a lot of time (no more arguments on code styles and on which paradigm to use) and made code simpler.

What's wrong with fn (r Repo) find_by_id(id int)? fn (user User) is_bob() can also be chained.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 4

@slimsag @boyter I'll add an example with Generics soon.

@rrjanbiah yes, but coroutines and channels have to be implemented first.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 4

I myself find u16 much more readable than uint16.

I think one of the unwritten rules of V is the fewer unnecessary keystrokes, the better. But within reason! (looking at you, Perl)

That's why there's no : between names and types, no -> before return types, no , in struct initializations, etc.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 4

Are things like ifs and blocks expressions?

I think it's very important for languages with immutability by default to have if expressions:

a := if condition { value1 } else { value2 }

vs

mut a := 0
if condition {
    a = value1
} else {
    a = value2
}

from v.

oliwer avatar oliwer commented on May 18, 2024 3

from v.

medvednikov avatar medvednikov commented on May 18, 2024 3

@scurest not right now, but they will be.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 3

from v.

medvednikov avatar medvednikov commented on May 18, 2024 2

Hi @xorhash

Thanks a lot for your detailed feedback!

You may already want to put special thought into the HTTP library since I assume it will see a lot of usage.

Absolutely!

How do you return failure from main?

Just like in go: os.exit(1)

However, I'm not sure if an implicit $err is always the right solution. I'm worried about localization and internationalization of error messages in particular.

err value is set with return error(error_msg). Error message can be fetched via gettext for example.

Looking at the printing examples: Is it accurate that a variable identifier can be any character.

Can you explain what you mean?

What happens if you try to println() an array type? My intuition would suggest that a compilation error would have the least surprise, but you may want to do something else.

println supports everything that has a str() method. Arrays have that predefined.
So println([1,2,3]) will print [1, 2, 3] :)

What even is an int? Is that a literal C int with all the risks of undefined behavior on signed overflow? Are unsigned types even available?

Very good question. This will be one of the first topics in the docs. V has byte, i16, int, i64, u64 etc.

int is not like in Go or C. It's always an i32. I just personally prefer int to i32. It's used in approximately 80% of cases in Go stdlib, so I made an exception for it. It goes against the philosophy of the language, but I really like having int instead of i32.

Are you making any guarantees about the C code output of a function that only does arithmetic on integer types and has no branching?

Could you please give an example?

Thanks

from v.

burlesona avatar burlesona commented on May 18, 2024 2

Also, off topic, but since the above is my first post on your project I just wanted to say hello! V looks fantastic, take the good stuff of Go and make it even better (to me anyway).

Would love to help if there’s any way I can. Please let me know :)

from v.

medvednikov avatar medvednikov commented on May 18, 2024 2

Hey @citycide

Thanks for your feedback!

Is this a typo in the generic example?

Yes, I initially called it retrieve :) Fixed.

Do you always have to return from the outer function? How do you just do default values, or equivalents for some of Rust's Option methods?

Can you write a small example?

Is there any short lambda syntax? (a, b) => a + b

Not yet. Most likely there will be one. Need to think about the details.

How about pattern matching? It's one of my favorite features in Rust and ML languages, especially F#.

Same :)

from v.

medvednikov avatar medvednikov commented on May 18, 2024 2

@dsnippet

  1. No, it will be exactly like Go. Structs, embedding, interfaces.
  2. Yes. Again, same as Go.
  3. Yes.
  4. No.
  5. Right now you can call any C function:
    C.puts('hello world!')

from v.

xorhash avatar xorhash commented on May 18, 2024 2

The documentation tipped me off to it, but why does const API_URL = 'https://vlang.io/users.json' use = rather than :=?

It also seems weird to name a while loop for. Maybe just call it loop since the condition is optional?

from v.

medvednikov avatar medvednikov commented on May 18, 2024 2

@xorhash simplicity and consistency with other languages.

Having only one keyword instead of three (for, while, loop) helps keep the language smaller and simpler.

:= is used for variable declaration, const is for consts.

from v.

andrenth avatar andrenth commented on May 18, 2024 2

Do you plan to introduce constructors in V? My biggest issue with go is the lack of constructors due to default empty values for everything. It’s effectively impossible to prevent instantiation with invalid state.

from v.

andrenth avatar andrenth commented on May 18, 2024 2

@medvednikov, building on the example @oliwer gave, one can always do t := Thing{} and bypass constructor validation.

from v.

andrenth avatar andrenth commented on May 18, 2024 2

In rust it’ll work in the same module, but not from outside. Uninitialized fields + private access works fine to protect against invalid state.

Of course, your own module can create invalid state, but the point is preventing users of your library to do it.

from v.

oliwer avatar oliwer commented on May 18, 2024 2

from v.

jkaye2012 avatar jkaye2012 commented on May 18, 2024 2

Options are not error handling! Or at least not only, though some errors can be modeled with them.

Error handling should surely be strongly integrated into the language, (thus why I asked my first question to you about if errors will be extended past strings!), but I don't think that the option type should be conflated with that. They do share some similarities (e.g. you may want to be able to chain computations without handling every step in between), but are also quite different (e.g. you will often want to be able to coalesce different error types together for easier handling).

Maybe it might make more sense to talk a little more holistically about what your plans are for the error handling system? It could make the conversation a bit more concrete.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

As for ', I want to have only one main way of denoting strings. V will automatically replace " with ' if there are no apostrophes in it.

I might remove " entirely and force developers to just escape apostrophes.

' strings can have $vars and newlines, unlike all other languages, yes.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

@International no.

V compiler used to rely on generics to implement arrays and maps, but then I replaced them and made the code smarter (similar to Go's slices and maps).

There will be a way for users to create their own generic containers without generating extra code for each type and slowing down compilation time.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

I've added an example with generics:

https://github.com/vlang-io/V/blob/master/examples/generic_repository.v

from v.

haltcase avatar haltcase commented on May 18, 2024 1

I think it looks pretty good. I like a lot of the semantic choices you've made even if I don't particularly like some of Go's syntax that you're inheriting. I'm sure I'd get used to it though (except []User vs User[] that'll drive me insane 😆).

Immutability and first-class optionals (T?) with those nifty or blocks are great. I'm not sure I fully understand the way the or block works though. Do you always have to return from the outer function? How do you just do default values, or equivalents for some of Rust's Option methods?

It's going to be either pub or some symbol like in Oberon.

Either of these is good IMO, I don't like Go's capitalization rule for exports.

Backticks are used for characters. Which feels a bit weird.

It's probably best to use double quotes for strings and single quotes for characters but I understand the ease of typing isn't great for you. I use single quotes in JavaScript/TypeScript all the time but with everything else I code in using doubles, it can be a bit of a context switch.

Is there any short lambda syntax? (a, b) => a + b

How about pattern matching? It's one of my favorite features in Rust and ML languages, especially F#.


Is this a typo in the generic example? Should retrieve be find_by_id here:

https://github.com/vlang-io/V/blob/50d81e6edf1b7ed3c1ba0e24ce2c4b659b648b5a/examples/generic_repository.v#L34

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

@Francesco149 this is a discussion about the syntax of the language.

V can compile to machine code directly, as well as to C.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

I know, that's different. But due to the design and philosophy of the language, it's a necessity.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

@Tim-St V is being designed also for game development and science. Since conversions have to be implicit, there will be lots of float64( in the code. I think it's too verbose.

As for the function keyword, let's put it to vote:
https://twitter.com/vlang_io/status/1099687714042114049
:)

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

@burlesona what you described is nice and works well in a language like Ruby.

V has a very different philosophy. Everything is transparent and straightforward. If a function is called, it should be clear.

Since there are no constructors, would you automatically call these validators on every .age = ... and {age:... ? :)

This reminds me of notorious C# properties where a simple user.Name could call an unlimited amount of functions and it would never be visible to the caller.

from v.

burlesona avatar burlesona commented on May 18, 2024 1

Sorry, I left this thought out of my write up:

I think it would be reasonable for every struct to have a ‘new’ function, and that it’s a no-op by default, but can be explicitly defined to put initialization behavior in if needed.

However I agree with you about the strictness and explicitness you’re going for. I think a declarative set of constraints on the properties of a struct is a nice way to handle this concern, that way there’s one and only one way that a property can have behavior, that’s using the constraints on the declaration.

Hope that makes sense.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

Or I could just do what Rust does and not allow constructing using field names if there's at least one private field :)

In any case, it's a very important feature, and it will be implemented. Thanks for mentioning this @andrenth

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

No protection of fields and package variables was one of my biggest gripes with Go.

The fact that it's possible to do stuff like this os.Args = append(os.Args, "lol") is ridiculous.

from v.

scurest avatar scurest commented on May 18, 2024 1

Are things like ifs and blocks expressions?

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

@PavelVozenilek your second variant already works in V. It's just like in Go.

@jkaye2012 you can do for num in numbers, I decided to leave the C-style loop as a safer alternative to while.

from v.

medvednikov avatar medvednikov commented on May 18, 2024 1

I never said options are error handling only. In V option == Rusts's Result + Option. So maybe I should just chose a better name.

You can return error strings (return error('foo')) or error values (return os.NOT_FOUND).

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Or did you mean what happens if we call print([]User)? Compilation error :)

from v.

slimsag avatar slimsag commented on May 18, 2024

Will you keep raw string literals (backticks) from Go?

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@burlesona JSON is very popular right now, and it has lots of ".

Also to me ''s look cleaner in the code and don't require shift.

But I had your exact thoughts, maybe I'll do that. Just to be consistent with other languages.

@slimsag ' behaves exactly like ` in Go.

from v.

burlesona avatar burlesona commented on May 18, 2024

@medvednikov yeah it’s kind of tricky to balance, I think this is why most languages have a few string literal options :)

Maybe a block string literal syntax like Ruby’s heredoc (or many other examples) is a viable alternative for things like JSON input? But it does result in more than one way to make a string, which I agree isn’t perfect.

Or finally, less easy to type, but you could just go straight for backticks or some other universal string delimiter.

from v.

slimsag avatar slimsag commented on May 18, 2024

@medvednikov Not true -- ' is a rune literal in Go and cannot contain multiple characters nor newlines and supports escape codes, in contrast ` is a raw string literal supporting multiple characters and newlines with no support for escape codes

from v.

oliwer avatar oliwer commented on May 18, 2024

Hello @medvednikov and congratulations for V! It seems to solve many of Go's issues and could be a nice "better C". A few questions:

  • since your functions are all lowercase, how do you differentiate between local and exported functions?
  • can you tell us more about the what the runtime will do and how do you handle memory management compared to Go?
  • how do you declare a character/rune if ' is for strings? I understand that typing " is impractical with your keyboard, but you're a minority. Best thing you could do is switch ' and ", but I guess most people would hate it.
  • the error handling handling looks great! I also wanted to use or as that's what we often use in perl.
  • will V handle generics? :trollface:

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Hi @oliwer

since your functions are all lowercase, how do you differentiate between local and exported functions?

It's going to be either pub or some symbol like in Oberon.

  • can you tell us more about the what the runtime will do and how do you handle memory management compared to Go?

There's no runtime, memory management will be similar to Rust.

  • how do you declare a character/rune if ' is for strings? I understand that typing " is impractical with your keyboard, but you're a minority. Best thing you could do is switch ' and ", but I guess most people would hate it.

Backticks are used for characters. Which feels a bit weird.

Are there keyboard layouts that don't require shift to write "?

  • the error handling handling looks great! I also wanted to use or as that's what we often use in perl.

Thanks! I did some Perl in the past, that might have influenced the syntax :)

will V handle generics? :trollface:

It already does.

from v.

slimsag avatar slimsag commented on May 18, 2024

Would be cool to see an example of generics somewhere!

from v.

boyter avatar boyter commented on May 18, 2024

+1 to seeing some example of generics.

from v.

rrjanbiah avatar rrjanbiah commented on May 18, 2024

OT: Will it be possible to convert Go code to V?

from v.

International avatar International commented on May 18, 2024

Hi @medvednikov , does volt also use generics in it's codebase? Does it affect the compile times much?

from v.

Francesco149 avatar Francesco149 commented on May 18, 2024

I'm very skeptical about the "400 KB compiler with zero dependencies" part. It just transpiles to C and then compiles with gcc/clang, doesn't it? that implies a dependency on gcc or clang. can the V compiler emit machine code by itself?

from v.

dsnippet avatar dsnippet commented on May 18, 2024

Saw a couple of user examples that you published. V Looks interesting.
Since I could not get enough information from the user examples, here are some basic questions that come to my mind.

  1. Does V (plan to) support OOP, inheritance, polymorphism etc?
  2. Parallelism?
  3. Systems Programming, as in support for pointers etc.
  4. Garbage collection?

I would be glad if you can point me to some documentation.

from v.

dsnippet avatar dsnippet commented on May 18, 2024

Also ABI compatibility with C/C++. Is it possible to link in C/C++ functions directly in V?

from v.

awesomekyle avatar awesomekyle commented on May 18, 2024

As someone who spends 98% of his time in C & C++ and has no experience with Go, any sort of "V for C Programmers" documentation would be very helpful. 😄 Looking forward to seeing V's release!

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@awesomekyle V is language that can be learned in an hour.

A documentation/tutorial page will be out soon, and you'll see ;)

from v.

lasselindqvist avatar lasselindqvist commented on May 18, 2024

users.v says "V will automatically format and align your code." Does this mean the compiler changes the source files by formatting them?

from v.

xorhash avatar xorhash commented on May 18, 2024

Looking at the printing examples: Is it accurate that a variable identifier can be any character.

Can you explain what you mean?

Sorry for the late response. I'd sent the message without finishing my thougts there. Is it accurate that a variable identifier can be any character that is printable and not $ or .? What is the source code file encoding (or is it always based on system locale)?


Are you making any guarantees about the C code output of a function that only does arithmetic on integer types and has no branching?

Could you please give an example?

Assume you have a section of code like this:

// a through d are mutable u32
a *= b;
c *= d;
b += c;
d += a;
// ...more operations...

Do you make any guarantees that the exact equivalent C code is output? That might be useful for cryptography, though it's already hard enough to write cryptographic algorithms in C in a way that your compiler doesn't sabotage you.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@lasselindqvist

users.v says "V will automatically format and align your code." Does this mean the compiler changes the source files by formatting them?

yes

@xorhash

Is it accurate that a variable identifier can be any character that is printable and not $ or .?

No, letters, numbers, and underscore only.

What is the source code file encoding (or is it always based on system locale)?

UTF-8

Do you make any guarantees that the exact equivalent C code is output?

You mean, will the resulting C code be the same? Right now, yes.

from v.

ducdetronquito avatar ducdetronquito commented on May 18, 2024

@lasselindqvist

users.v says "V will automatically format and align your code." Does this mean the compiler changes the source files by formatting them?

yes

Linting code tends in general to be done by an external tool; and IDE, a CLI tool, etc...
It could be beneficial for the V compiler to avoid doing task that it do not needs to do :)

from v.

rrjanbiah avatar rrjanbiah commented on May 18, 2024

OT: Perhaps all OT should be in https://groups.google.com/forum/m/#!forum/vlang

from v.

loic-sharma avatar loic-sharma commented on May 18, 2024

V is looking great, I can't wait to play around with the language when it's released! :)

I don't have a background in Go, so this syntax feels strange to me:

fn (r Repo) find_by_id(id int) T? {
  ...
}

What're your thoughts on Uniform Function Call Syntax? Something like:

fn find_by_id(r Repo, id int) T? {
  ...
}

// Can be called two ways:
users_repo := new_repo<User>(db)
find_by_id(users_repo, 1) // No syntactic sugar
users_repo.find_by_id(1) // With Uniform Function Call syntactic sugar

This is great for method chaining too:

fn is_bob(user User) bool {
  return user.name == 'bob'
}

if users_repo.find_by_id(1).is_bob() {
  println('Hello Bob')
}

from v.

loic-sharma avatar loic-sharma commented on May 18, 2024

That's a fair response. There's nothing wrong with the existing syntax, it's just different for people that aren't used to Go. I'm sure we can get used to it 😄

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Just published the first draft of the documentation. So far it covers the basics.
https://vlang.io/docs

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@oliwer updated, thanks.

from v.

 avatar commented on May 18, 2024

The language looks very promising!
In my opinion code is easier to read when the types are named int, intX, uint, uintX, float32, float64 and fn is named func (like in Go). And also the string syntax should be the same like in Go (in my opinion).

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@andrenth can you please post a simple example of instantiation with invalid state? Thanks.

from v.

oliwer avatar oliwer commented on May 18, 2024

@andrenth

My biggest issue with go is the lack of constructors

But you can already write constructors in Go (and I assume it's the same for V):

func NewThing(a, b string) *Thing {
  return &Thing{a: a, b: b, c: "default"}
}

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Indeed, constructors are basically functions. Why complicate the language and introduce another construct.

Just like Go and Rust, V is not an OOP language. It has structs and powerful interfaces, that's it.

from v.

andrenth avatar andrenth commented on May 18, 2024

But in rust it works fine because you can’t directly instantiate a struct with private fields except via a function. In go you can bypass the function because everything has a default value.

I’m not asking for the language to be more complicated, just for a way to ensure the function(s) that build an object must be called.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@andrenth ok I see your point. I'll think about it.

from v.

andrenth avatar andrenth commented on May 18, 2024

In Go:

type User struct {
  name string
}

func NewUser(name string) (*User, error) {
  if name == “” {
    return nil, InvalidUserErr
  }
  return User{name}, nil
}

// But one can do and create invalid state.
u := User{}

I think the core issue is “uninitialized fields/variables should be a compile time error instead of automatically initialized with an empty value”.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Indeed, in Rust this won't compile:

struct Foo {
    a: u8,
    b: u32,
    c: bool,
}

let foo = Foo { a: 0, c: false };
missing field `b` in initializer of `Foo`

By the way, you wrote that

you can’t directly instantiate a struct with private fields except via a function

But this is perfectly legal:

let foo = Foo { a: 0, b: 1, c: false };

Making uninitialized fields illegal won't really help since it won't prevent instantiation with invalid state:
u := User{""} can still be invalid.

This is an interesting problem.

from v.

burlesona avatar burlesona commented on May 18, 2024

@medvednikov I have some thoughts on the initialization problem.

I know you're not looking to build an OOP oriented language, but in practice every language that has "objects" or "structs" in it faces some of the basic OOP issues. I've worked in Ruby for a long time, and it has some nice, elegant handling for many of these cases.

In Ruby you always make an object by calling the new method on the class. The class then calls the initialize method on the object. These both exist by default with no arguments required, and you never have to define them, thus for any class you always just call MyType.new to create an object.

However you can hook into these methods and modify them however you want, and since this is the only way that an object can be created, it gives you great control over how things get initialized. Now I'll ignore the "class" side of this since you don't want to go for OOP, but here's an example of what you could do at the instance level.

In the simplest case you just define required arguments by listing them in the initialize method, and provide default values if you want.

class Person
  def initialize(first_name:, last_name:, age:, team: Team.default)
    @first_name = first_name # this assigns the local variable to an instance variable
    @last_name = last_name
    @age = age
    @team = team
  end
end

p = Person.new(first_name: 'Example', last_name: 'Person', age: 32)
p.age #=> 32
p.team #=> returns the default team

That's not very interesting, but inside that method it's just code, you can do whatever you want to validate the arguments, ie.

class Person
  def initialize(first_name:, last_name:, age:, team: Team.default)
    @first_name = first_name.to_s # ruby is dynamically typed but you can coerce like this
    @last_name = last_name.to_s
    @age = age.to_i
    @team = team
    raise ArgumentError, 'First name must be at least 1 character' if @first_name.length < 1
    raise ArgumentError, 'Last name must be at least 3 characters' if @last_name.length < 3
    raise ArgumentError, 'Age must be positive' if @age < 0
  end
end

That works well but it's a little verbose, so a common pattern in ruby is to define macros to automate assignment and validation of initialization arguments. Skipping the implementation details, the result looks like this:

class Person
   argument :first_name, String, constraint: -> s { s.length > 0 }
   argument :last_name, String, constraint: -> s { s.length > 2 }
   argument :age, Integer, constraint: -> i { i > 0 }
   argument :team, Team, optional: true, default: -> { Team.default }
end

The idea here is that you list arguments, their type, and then you can set some options like whether a value is optional, and anonymous functions to validate the input or provide the default value.

Extending that kind of extended type definition seems like it could be a nice solution for V. I could see a very similar syntax working nicely, for example:

struct Person {
    first_name: string, constraint: -> s { s.length > 0 }
    last_name: string, constraint: -> s { s.length > 2 }
    age: int, constraint: -> i { i > 0 }
    team: Team, optional: true, default: -> { Team.default() }
}

Now you've got a straightforward, declarative definition of how a struct gets created that should handle the issues in the discussion above. Even better you can

No worries if that's not your cup of tea, just thought I'd throw that out there for you to consider :)

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@andrenth

Would read-only struct fields solve this problem?

user.name can be accessed, but user.name = "" or User{name:""} would result in a compilation error.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Or I guess you could call them only when the struct initialization syntax is used.

from v.

burlesona avatar burlesona commented on May 18, 2024

To reply more specifically to “should these be called on every .age=“

I would say yes, but keeping in mind that your ability to declare constraints is pretty limited (in a good way).

But let’s explore what you’ll need if you don’t do that.

Can you define custom types with some constraint behavior? If so, simply making an Age or FirstName type with the relevant constraints would have the same effect.

But since v has no nil, what do you do if a struct is created and some properties are undefined?

from v.

medvednikov avatar medvednikov commented on May 18, 2024

But since v has no nil, what do you do if a struct is created and some properties are undefined?

They are initialized to zero values (0 for int, "" for string, Foo{} for struct)

I suggested read-only struct fields. Maybe even by default. They solve this problem.

from v.

burlesona avatar burlesona commented on May 18, 2024

Yeah I agree properties being read only is a nice way to handle that. I will say though it might be nice to have a shorthand for “copy and change,” for cases where you have a lot of properties and need to make a new object with some changes.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

from v.

andrenth avatar andrenth commented on May 18, 2024

I don’t see how read-only fields solve the invalid state problem if they’re initialized to zero values, but maybe I didn’t understand the proposal.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@andrenth getting rid of automatic initialization won't solve the problem because people still can do things like u := User{name:""} and not initialize the object correctly.

Besides, it's a key feature of the language, lots of functionality relies on it. For example, Config structs which are used as a replacement for named arguments.

Making all fields in User read-only would make the example above not compile. However it would still be possible to do u := User{}.

Y could make User unexported, but leave its fields exported. It actually works in Go:

type user struct {
        Age int
}

func NewUser() user {
        return user{10}
}

 u := foo.NewUser()
 fmt.Println(u.Age) // 10
 u2 := foo.user{} // cannot refer to unexported name foo.user

There's a big problem with this approach though: now it's impossible to reference foo.User. We can't have foo.User arguments or struct fields.

So the only solution I can think of is to add a flag that doesn't allow a struct to be manually initialized.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Or I could make it super strict: if there's at least one read-only field, manual initialization is not allowed, since technically User{} is the same as User{name:""}. But name is read-only and cannot be set from an outside module.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

Just added an example with some concurrency and mandatory locking:

https://github.com/vlang-io/V/blob/master/examples/concurrent_news_fetcher.v

from v.

calebwin avatar calebwin commented on May 18, 2024

Could you explain the func main() { and go fn() {?
Have you yet settled on a keyword for functions and just not updated it everywhere?

from v.

medvednikov avatar medvednikov commented on May 18, 2024

from v.

medvednikov avatar medvednikov commented on May 18, 2024

from v.

oliwer avatar oliwer commented on May 18, 2024

Interesting! Does that mean all access to variables defined outside the current thread must protected by a lock?

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@oliwer yes, as long as they are mutable of course.

from v.

scurest avatar scurest commented on May 18, 2024

Cool, thanks!

In the example code on the language comparison page, the V example contains

for cursor < ids.len {
        lock { // Without this lock block the program will not compile
                id := ids[cursor]
                cursor++
        }
        ...

If another thread has incremented cursor between the check in the for loop and taking the lock, the subscript ids[cursor] might be out-of-bounds. Can this happen?

I also wrote a simpler Rust implementation.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@scurest thanks, I'll update both examples.

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@scurest you implementation uses crossbeam, which is not part of Rust. The implementation I posted also uses 3rd party libs (it depends on hyper and rustc_serialize).

I understand Rust's philosophy of promoting use of 3rd party packages,
but after giving it a thought I realized that it's more fair to Go and V to only use the tools the language provides.
But is it even possible with Rust? It looks like even JSON serialization is not part of stdlib.

You can run the V and Go examples just by downloading the source and building it.
Rust version (including the one I originally posted) requires installation of 3rd party packages.

P.S. I'm clearly not a Rust dev ;)

from v.

medvednikov avatar medvednikov commented on May 18, 2024

from v.

PavelVozenilek avatar PavelVozenilek commented on May 18, 2024

From the docs:

if ... the braces are always required

This would make chain of ifs awkward:

    if a {
    } else {
        if b {
        } else {
            if c {
            } else {
            }
        }
    }

If expressions would feel even worse.

This could be fixed in two ways:

  1. Adding elif keyword (preferred by me).
  2. Adding exception to "always braces" rule.

Variant [1] would look like:

    if a {
    } elif b {
    } elif c {
    } else {
    }

Variant [2] would look like:

    if a {
    } else if b {
    } else if c {
    } else {
    }

from v.

jkaye2012 avatar jkaye2012 commented on May 18, 2024

Manual index management in for loops feels like an eternal source of bugs that has existed forever. Why not use iterators/ranges instead? Or maybe this exists but it's just not documented yet? Given the auto-enumeration that's documented, I'm not sure that the c-style loop is ever really required? I do like that there is only a single looping construct though.

from v.

PavelVozenilek avatar PavelVozenilek commented on May 18, 2024

@medvednikov: One more thing. It will be tempting to use if expression to isolate initialization code, like:

    a := if true {
             mut this := ...
             mut that := ...
             ... // lot of calculations
             42;
           }

For such situations just a block could be used:

    a := {
           mut this := ...
           mut that := ...
           ... // lot of calculations
           42;
         }

from v.

 avatar commented on May 18, 2024

Hi,
First of all thank you for what you're doing, I can't wait for V to be open-sourced !

Concerning the " vs ' debate, one argument for using " to delimit strings is the fact that some languages make use of ' in sentences (I am thinking about french or italian for instance).

str := "J'ai mangé un gâteau qui n'était pas frais"

VS

str := 'J\'ai mangé un gâteau qui n\'était pas frais"

What about HTTP/2 ?
Do you plan to implement it ?
I am curious about how you would serve HTTP/2 ressources in V.

from v.

haltcase avatar haltcase commented on May 18, 2024

@medvednikov coming back to our discussion about options some time ago:

Do you always have to return from the outer function? How do you just do default values, or equivalents for some of Rust's Option methods?

Can you write a small example?

Well essentially how would you translate all/any of Rust's or F#'s Option methods into V? The Rust docs page I linked has tons of examples.

In Rust:

Some("four").map(|value| value.len())
// -> Some(4)

None.map(|value| value.len())
// -> None

fn vikings() -> Option<&'static str> {
  Some("vikings")
}

Some("barbarians").or_else(vikings)
// -> Some("barbarians")

None.or_else(vikings)
// -> Some("vikings")

In F#:

Some "four"
|> Option.map String.length
// -> Some(4)

None
|> Option.map String.length
// -> None

let vikings =
  Some "vikings"

Some "barbarians"
|> Option.orElseWith vikings
// -> Some("barbarians")

None
|> Option.orElseWith vikings
// -> Some("vikings")

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@citycide none of these are supported.

Can you give me an example where you need to use Some("four").map(|value| value.len()) ?

from v.

haltcase avatar haltcase commented on May 18, 2024

@medvednikov the point is you can operate on the value if it's present without needing to check if it's nil or missing, which is the entire proposition behind optional types / Option.

Obviously you wouldn't typically create a Some with a literal value like that before operating on it, the value would come from somewhere else. Then at the end of such a chain of maps/filters etc. you'd use pattern matching or something like defaultValue "some default" to branch on Some x vs None.

It seems so far that the doSomething()? you've got here then is more like try { doSomething() } catch { return }. Is that the extent of the feature?

from v.

jkaye2012 avatar jkaye2012 commented on May 18, 2024

Wouldn't it be possible to build options in as part of the standard library rather than as a core part of the language itself? At least without F#'s pipe operator.

I also see their usefulness and use them regularly with other languages, but a rich standard lib could be a better place. Perhaps a functional core library with other concepts to support purity could be nice as well (and something that I would be more than willing to contribute).

from v.

medvednikov avatar medvednikov commented on May 18, 2024

@citycide right now yes, the optionals are only used for error handling. I didn't like Go's err != nil checks, so I implemented very simple optionals to handle them.

As you might have guessed I don't have much experience with them as I've never used a language with optionals before :)

@jkaye2012 I want them to be part of the language for simplicity and to avoid generics. Error handling is an integral part of development, and I want there to be one official way of doing that.

from v.

haltcase avatar haltcase commented on May 18, 2024

@medvednikov I see that it incorporates Rust's Result, but I really don't see any Option functionality at all which is what I've been trying to explore.

from v.

medvednikov avatar medvednikov commented on May 18, 2024
fn foo() Foo? {
  if ... {
     return None
  }
  return Foo{}
}

foo := foo() or {
   println('None')
   return
}

That's the only functionality right now.

from v.

haltcase avatar haltcase commented on May 18, 2024

I'll just have to see how it develops I guess — it's far better than Go's if err != nil so I'm with you on that.

from v.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.