Skip to content

Instantly share code, notes, and snippets.

@krolaw
Last active August 18, 2019 02:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krolaw/000a8bfb72483880a691dcbd00ae4919 to your computer and use it in GitHub Desktop.
Save krolaw/000a8bfb72483880a691dcbd00ae4919 to your computer and use it in GitHub Desktop.
V safe and easy

Simple rules for V safety

Goal is to be as safe as rust but without the complexity. This document may be totally naive, so please give constructive feedback.

Concurrency

  1. Once a mutable variable is passed to a coroutine function or channel it can no longer be used.
  2. Variables received by coroutine closures must be immutable
  3. Channels are considered immutable

If you need the variable back, it needs to be passed back, i.e. by channel. This prevents reading and writing to the same variables on different threads. Locks are unnecessary.

Example 1 - Coroutine with closure

x := 3
mut y := 7
y++ // y == 8
go fn() {
  for i := 0 ; i < x ; i++ { // a.o.k, x is immutable
    println(x) // a.o.k
    println(y) // compile error, var not immutable
  }
}

Example 2 - Channels

jobs := make_chan<int>(0)
routines := 5
jobsPerRoutine := 10
waitGroup := make_chan<none>(0)

for i := 0 ; i < routines ; i++ {
  go fn(i) {
    for j := 0 ; j < jobsPerRoutine ; j++ {
      jobs <- j
    }
    waitGroup <- none
  }
}

go fn() {
  mut count := 0
  for _ in waitGroup {
    count++
    if count == routines {
      jobs.close()
      break
    }
  }
}

for i in jobs {
  println(i)
}
mut x := 7
x++
y := x // copy of x before passing to coroutine
go fn(mut i) {
  i++
  print(i)
}(mut x) // if mut missing, compiler error (needs to be obvious at both ends what is happening)

z := x // Compiler error, x sent to coroutine - use y the copy instead
x = 9 // Compiler error, x sent to coroutine.

mut b := 7
c <- b
z := b // Compiler error!

This means you can pass mutable data around over channels etc, but you can't touch it once it's sent. It needs to be sent back to you if you want to reuse it, otherwise take a copy before sending it on.

Mutability and Immutability syntax (no pointers)

Immutable by default

a := 1 // a is immutable
mut b := a // b is mutable, b == 1, a == 1
b++ // b == 2, a == 1

Deferences pointers by default

mut a := 1 // a is mutable, a == 1
mut b := &a // b is a mutable reference and points to a
mut c := b // c is mutable, c == 1 (c is a value and does NOT reference a or b)
mut d := &b // d is a mutable reference and also points to a (where b points to)
mut e := &&b // reference to reference, not high on implementation priorities and only allowed in code marked unsafe.

References cannot reference immutable objects (use a value)

a := 1 // a is immutable
mut b := &a // totally illegal
mut c := a // c == 1

Thanks for your time.

@krolaw
Copy link
Author

krolaw commented Aug 2, 2019 via email

@runeimp
Copy link

runeimp commented Aug 2, 2019

Thanks for the explanation. 😉

@grd
Copy link

grd commented Aug 3, 2019

The section about "Mutability and Immutability syntax (no pointers)", I think it's very, very scary!

How can you explain the following line:

d++ // d == 2, but b still == 1

to anyone who isn't an expert, and even an expert? This is probably worse than advanced C. To what data points d?

@grd
Copy link

grd commented Aug 3, 2019

And to think about the notion of go, I think that co would be a better name since it is a co-routine after all. Go is only named this way because of Google employees and to inherit this, I think is a bad choice.

@krolaw
Copy link
Author

krolaw commented Aug 4, 2019

@grd
mut d := b // d is mutable and receives the value of b
d++ // d == 2, but b still == 1

This is because d receives b's value (no mut), so it's not sharing the memory, it gets its own copy. Quite different to the below.

mut d := mut b // d is mutable and receives a reference to b (not the extra mut)
d++ // d == 2, and b == 2

@grd
Copy link

grd commented Aug 4, 2019

All I am saying is that this just doesn't feel right to me, and I think that you introduced a can of worms. To be honest I don't know the answer but this solution isn't the right one if you ask me.

@krolaw
Copy link
Author

krolaw commented Aug 4, 2019

@grd Rust is safe, so I think they are on to something by dropping pointers. And they justify massive complexity and not so fast compilation speed for this safety.

However, if this logic is sound (safe), much of the complexity drops away, and won't hurt V's compilation speed.

It won't be "safe, fast, easy, choose any two" any more.

@grd
Copy link

grd commented Aug 4, 2019

I don't know what you mean with your comment. We all know that Rust isn't safe and won't ever be safe. Rust is an attempt to be safe. That's very different than safe. Just look at all the unsafe parts and assembly code in Rust. And the more people are working onto it, the more unsafe it will become.
I don't know what you mean with "safe, fast, easy, choose any two"? The answer of that question should always be safe and easy. It shouldn't be a question if you ask me.
But let's return to my can of worms. So with mut you are willing to create a pointer and a value. That is cool to me. The problem that I have is assigning the mut to another mut because then the sh*t can hit the fan. That is what I mean.

@krolaw
Copy link
Author

krolaw commented Aug 4, 2019

@grd Safe rust, that is pure rust, is safe. Unsafe rust is generally only use to interface other languages. I'm not saying rust is bug free, but conceptually, pure rust is safe - i.e. has no undefined behaviour.

V's roadmap also includes no undefined behaviour.

Passing mutable objects is sometimes necessary. Can you give me an example of it "hitting the fan" with respect to the provided proposal?

@grd
Copy link

grd commented Aug 4, 2019

I have said it in my first comment: To what data points d?
And how can you explain that?

@krolaw
Copy link
Author

krolaw commented Aug 4, 2019

One (mut == PTR) interpretation might be:

mut b := 1 // b := &int(1) so *b == 1
mut d := b // d := &int(*b) so *d == 1 but at different mem location
d++ // (*d)++ so *d == 2, but *b still == 1

But I prefer:

mut b := 1 // b := 1
mut d := b // d := b so d gets a copy of b
d++ // d++ so d == 2, but b still == 1

Adding a 2nd mut shares the same memory instead

mut b := 1 // b := &int(1) so *b == 1
mut d := mut b // d := b so d == b therefore *d == 1
d++ // (*d)++ so *d == 2, and so does *b

But another way is:

mut b := 1 // b := 1
mut d := mut b // d := &b so d points to b's location therefore *d == 1
d++ // (*d)++ so *d == 2, and so does b

Hope that helps.

@grd
Copy link

grd commented Aug 5, 2019

My idea is to disallow d. The problem arise when you create a mut from another mut. When you disallow this then I think the problem is solved. I agree with your mentality about having this but I don't think that this should be taken place to begin with. So if you ask me, I think that the compiler should disallow this. Then you are rid of the ambiguity and if you ask me that would be a good solution.

@krolaw
Copy link
Author

krolaw commented Aug 5, 2019

@grd that would cripple a lot of perfectly acceptable code.
mut b := some_func()
mut a := some_other_func()
if b.some_method(a) {
a = b
}
.... // code that works on a

@grd
Copy link

grd commented Aug 5, 2019

You are saying "a lot". Can you quantize it? How much code of this is / should be running in the field?

@Compro-Prasad
Copy link

Compro-Prasad commented Aug 5, 2019

IMO having mut e := ref b in place of mut e := mut b would be better to have and won't confuse users. This will also help in having e := ref b as an immutable reference.

@grd
Copy link

grd commented Aug 5, 2019

Why are you wanting this? Can you quantize it?

@krolaw
Copy link
Author

krolaw commented Aug 5, 2019

@grd Can you indicate how mut d := mut b is unsafe? To disallow something, there should be a valid reason. It's quite common in other languages to pass one ptr (or object) to another variable. In terms of quantity, I'll concede I'm not sure. I want as much flexibility as practical within a hard constraint of safety. If you can show me how safety is broken and I'll change my view.

@grd
Copy link

grd commented Aug 5, 2019 via email

@krolaw
Copy link
Author

krolaw commented Aug 5, 2019

@Compro-Prasad It's not always a reference.
mut e := mut b could mean e := b or e := &b depending on whether b is a ptr.
To me mut e := ref b could imply a ptr to a ptr if b is already a ptr, which isn't the case. Plus this would introduce another reserved word. using mut e := mut b enforces visually that both vars are mutable. mut e := ref b implies (to me) that b may or may not be mutable, which is also not the case.

This is just my view. I'm not in charge thankfully.

@grd
Copy link

grd commented Aug 5, 2019

Of cause this is your view. But this also shows how hard it is to comply with things that you( don't) want. The solution would be to do it like this:

z := b
mod d := z

That would be the answer that we are looking for. It ain't perfect but still it would be a lot better than what we have today.

@krolaw
Copy link
Author

krolaw commented Aug 5, 2019

@grd Thankfully V is not Go. But honestly, I want mut e := mut b because I believe it would be useful to me (and others), see previous example. Every language I've worked with allows passing of objects for editing in some way or another, so I don't feel alone. Therefore, without an adequate counter argument which challenges the V mandate, I think it's a good idea.

@krolaw
Copy link
Author

krolaw commented Aug 5, 2019

@grd

But this also shows how hard it is to comply with things that you( don't) want.

I don't understand. What are the hard things to comply with?

The solution would be to do it like this:

Sorry, I don't understand that there is a problem, yet alone what your solution is doing.

That would be the answer that we are looking for.

Sorry, I don't know what the question/problem is.

It ain't perfect but still it would be a lot better than what we have today.

Please expand on both what the problem is and how your solution solves it. I'm not aware of an issue, but I'm happy to have it explained to me.

@grd
Copy link

grd commented Aug 5, 2019

You are not the guy who is in charge of this, so do it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment