v
v copied to clipboard
Problems with string interpolation of `SomeStruct<T>` as well as methods on generic structs, returning `SomeStruct<&T>`
V doctor:
OS: linux, Ubuntu 20.04.3 LTS
Processor: 4 cpus, 64bit, little endian, Intel(R) Core(TM) i3-3225 CPU @ 3.30GHz
CC version: cc (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0
getwd: /v/vnew
vmodules: /home/delian/.vmodules
vroot: /v/vnew
vexe: /v/vnew/v
vexe mtime: 2022-11-05 12:04:10
is vroot writable: true
is vmodules writable: true
V full version: V 0.3.2 e81e0ac.27d8f23
Git version: git version 2.25.1
Git vroot status: 0.3.2-60-g27d8f237
.git/config present: true
thirdparty/tcc status: thirdparty-linux-amd64 12f392c3
What did you do?
v -g -o vdbg cmd/v && vdbg bCQbVgRp.v
module main
struct None {}
pub type Maybe<T> = None | T
pub fn (m Maybe<T>) str<T>() string {
return if m is T {
x := m as T
'Some($x)'
} else {
'Noth'
}
}
pub fn some<T>(v T) Maybe<T> {
return Maybe<T>(v)
}
pub fn noth<T>() Maybe<T> {
return Maybe<T>(None{})
}
pub fn (m Maybe<T>) is_some<T>() bool {
return match m {
None { false }
T { true }
}
}
pub fn (m Maybe<T>) is_noth<T>() bool {
return match m {
None { true }
T { false }
}
}
pub fn (m Maybe<T>) unwrap<T>() T {
if m is T {
return m
}
panic("can't unwrap Noth value")
}
pub fn (m Maybe<T>) unwrap_or<T>(noth_value T) T {
return match m {
None { noth_value }
T { m }
}
}
/// Boolean operators
pub fn (m Maybe<T>) and<T>(m2 Maybe<T>) Maybe<T> {
return match m {
None { noth<T>() }
T { m2 }
}
}
pub fn (m Maybe<T>) and_then<T>(f fn (v T) Maybe<T>) Maybe<T> {
return match m {
None { noth<T>() }
T { f(m) }
}
}
pub fn (m Maybe<T>) @or<T>(m2 Maybe<T>) Maybe<T> {
return match m {
None {
match m2 {
None { None{} }
T { m2 }
}
}
T {
m
}
}
}
pub fn (m Maybe<T>) or_else<T>(f fn () Maybe<T>) Maybe<T> {
return match m {
None { f() }
T { m }
}
}
pub fn (m Maybe<T>) xor<T>(m2 Maybe<T>) Maybe<T> {
if m is None && m2 is None {
return noth<T>()
}
if m2 is None {
return m
}
return m2
}
/// Modifier operations
//pub fn (m Maybe<T>) map<T,U>(f fn (v T) U) Maybe<U> {
// return match m {
// None { noth<U>() }
// T {
// some<U>(f(m))
// }
// }
//}
pub fn (m Maybe<T>) filter<T>(predicate fn (v T) bool) Maybe<T> {
match m {
None { return None{} }
T {
if !predicate(m) {
return None{}
}
return some<T>(m)
}
}
}
/// Utils methods
// pub fn (m Maybe<T>) flatten<T>() Maybe<T> {
// // TODO:
//}
pub fn (m &Maybe<T>) as_ref<T>() Maybe<&T> {
return match m {
None { noth<&T>() }
T {
mut ref := voidptr(unsafe { &m })
some<&T>(ref)
}
}
}
fn main() {
a := some(123)
b := some('abc')
println(a.str()) // works
println(b.str()) // works
println(a.unwrap()) // works
ra := a.as_ref() // compiles, but is buggy, since the inferred type is MayBe<int>, and NOT the correct MayBe<&int> .
explicit_ra := a.as_ref<&int>() // also compiles, but is buggy, since the inferred return type is `MayBe<int>`, and NOT the correct MayBe<&int> .
println(ra.str()) // works
println(ra.unwrap().str()) // works
println(explicit_ra.str()) // works
//
// c := a.@or(b) // cgen error
// println(a) // cgen error
// println(b) // cgen error
// println(ra) // cgen error
// println('$a') // cgen error
}
What did you expect to see?
A program that compiles even when the commented lines at the end are uncommented.
What did you see instead?
Some(123)
Some(abc)
123
Some(-134639648)
-134639648
Some(-134639648)
c := a.@or(b) should be a checker error, since the type of a is Maybe<int>, while the type of b is Maybe<string> i.e. incompatible.
println(a) should work, just like println(a.str()) does
println('$a') should work too, just like println(a.str()) does
ra := a.as_ref() not inferring that the return type should be Maybe<&int> and NOT Maybe<int> as it does, is the weirdest of all listed bugs here, and probably the most important as well, since imho it can not be worked around easily.
Uncommenting the pub fn (m Maybe<T>) map<T,U>(f fn (v T) U) Maybe<U> { method also leads to weird checker errors, that are not directly related to the map method:

In the original report, there was also this:
// pub fn (m Maybe<Maybe<T>>) flatten() Maybe<T> {
// return m.unwrap_or(noth<T>())
// }
... which I could not express with V, while it does serve a useful functionality 🤔 . Not sure what could be done about it.
The only remaining issue is:
ra := a.as_ref()
is not inferring, that the return type should be Maybe<&int> and NOT Maybedump( typeof(ra).name ) produces: [g.v:142] typeof(ra).name: Maybe<int>, but it should be:
[g.v:142] typeof(ra).name: Maybe<&int>
Current output:
PS D:\Games\Proekti\V\interactions> type test.v
module main
struct None {}
pub type Maybe[T] = None | T
pub fn (m Maybe[T]) str[T]() string {
return if m is T {
x := m as T
'Some(${x})'
} else {
'Noth'
}
}
pub fn some[T](v T) Maybe[T] {
return Maybe[T](v)
}
pub fn noth[T]() Maybe[T] {
return Maybe[T](None{})
}
pub fn (m Maybe[T]) is_some[T]() bool {
return match m {
None { false }
T { true }
}
}
pub fn (m Maybe[T]) is_noth[T]() bool {
return match m {
None { true }
T { false }
}
}
pub fn (m Maybe[T]) unwrap[T]() T {
if m is T {
return m
}
panic("can't unwrap Noth value")
}
pub fn (m Maybe[T]) unwrap_or[T](noth_value T) T {
return match m {
None { noth_value }
T { m }
}
}
/// Boolean operators
pub fn (m Maybe[T]) and[T](m2 Maybe[T]) Maybe[T] {
return match m {
None { noth[T]() }
T { m2 }
}
}
pub fn (m Maybe[T]) and_then[T](f fn (v T) Maybe[T]) Maybe[T] {
return match m {
None { noth[T]() }
T { f(m) }
}
}
pub fn (m Maybe[T]) @or[T](m2 Maybe[T]) Maybe[T] {
return match m {
None {
match m2 {
None { None{} }
T { m2 }
}
}
T {
m
}
}
}
pub fn (m Maybe[T]) or_else[T](f fn () Maybe[T]) Maybe[T] {
return match m {
None { f() }
T { m }
}
}
pub fn (m Maybe[T]) xor[T](m2 Maybe[T]) Maybe[T] {
if m is None && m2 is None {
return noth[T]()
}
if m2 is None {
return m
}
return m2
}
/// Modifier operations
// pub fn (m Maybe<T>) map<T,U>(f fn (v T) U) Maybe<U> {
// return match m {
// None { noth<U>() }
// T {
// some<U>(f(m))
// }
// }
//}
pub fn (m Maybe[T]) filter[T](predicate fn (v T) bool) Maybe[T] {
match m {
None {
return None{}
}
T {
if !predicate(m) {
return None{}
}
return some[T](m)
}
}
}
/// Utils methods
// pub fn (m Maybe<T>) flatten<T>() Maybe<T> {
// // TODO:
//}
pub fn (m &Maybe[T]) as_ref[T]() Maybe[&T] {
return match m {
None {
noth[&T]()
}
T {
mut ref := voidptr(unsafe { &m })
some[&T](ref)
}
}
}
fn main() {
a := some(123)
b := some('abc')
println(a.str()) // works
println(b.str()) // works
println(a.unwrap()) // works
ra := a.as_ref() // compiles, but is buggy, since the inferred type is MayBe<int>, and NOT the correct MayBe<&int> .
explicit_ra := a.as_ref[&int]() // also compiles, but is buggy, since the inferred return type is `MayBe<int>`, and NOT the correct MayBe<&int> .
println(ra.str()) // works
println(ra.unwrap().str()) // works
println(explicit_ra.str()) // works
//
// c := a.@or(b) // cgen error
// println(a) // cgen error
// println(b) // cgen error
// println(ra) // cgen error
// println('$a') // cgen error
}
PS D:\Games\Proekti\V\interactions> v version
V 0.4.4 febc4a7
PS D:\Games\Proekti\V\interactions> v run test.v
test.v:17:9: error: unknown type `Maybe[&int]`.
Did you mean `Maybe[int]`?
15 |
16 | pub fn some[T](v T) Maybe[T] {
17 | return Maybe[T](v)
| ~~~~~~~~~~~
18 | }
19 |
test.v:17:9: error: unknown type `Maybe[&string]`.
Did you mean `Maybe[string]`?
15 |
16 | pub fn some[T](v T) Maybe[T] {
17 | return Maybe[T](v)
| ~~~~~~~~~~~
18 | }
19 |
test.v:21:9: error: unknown type `Maybe[&int]`.
Did you mean `Maybe[int]`?
19 |
20 | pub fn noth[T]() Maybe[T] {
21 | return Maybe[T](None{})
| ~~~~~~~~~~~~~~~~
22 | }
23 |
test.v:21:9: error: cannot cast struct `None` to `Maybe[&int]`
19 |
20 | pub fn noth[T]() Maybe[T] {
21 | return Maybe[T](None{})
| ~~~~~~~~~~~~~~~~
22 | }
23 |
test.v:21:9: error: unknown type `Maybe[&string]`.
Did you mean `Maybe[string]`?
19 |
20 | pub fn noth[T]() Maybe[T] {
21 | return Maybe[T](None{})
| ~~~~~~~~~~~~~~~~
22 | }
23 |
test.v:21:9: error: cannot cast struct `None` to `Maybe[&string]`
19 |
20 | pub fn noth[T]() Maybe[T] {
21 | return Maybe[T](None{})
| ~~~~~~~~~~~~~~~~
22 | }
23 |
If the code of your project is in multiple files, try with `v .` instead of `v test.v`
If the code of your project is in multiple files, try with `v .` instead of `v test.v`
If the code of your project is in multiple files, try with `v .` instead of `v test.v`
If the code of your project is in multiple files, try with `v .` instead of `v test.v`