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

Problems with string interpolation of SomeStruct<T> as well as methods on generic structs, returning SomeStruct<&T> #16340

Open
spytheman opened this issue Nov 5, 2022 · 8 comments · Fixed by #16355 or #16403
Labels
Bug This tag is applied to issues which reports bugs. Generics[T] Bugs/feature requests, that are related to the V generics. Unit: cgen Bugs/feature requests, that are related to the default C generating backend. Unit: Checker Bugs/feature requests, that are related to the type checker. Unit: Type System Bugs/feature requests, that are related to the V types system.

Comments

@spytheman
Copy link
Member

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)
@spytheman spytheman added Bug This tag is applied to issues which reports bugs. Unit: cgen Bugs/feature requests, that are related to the default C generating backend. Unit: Checker Bugs/feature requests, that are related to the type checker. Unit: Type System Bugs/feature requests, that are related to the V types system. Generics[T] Bugs/feature requests, that are related to the V generics. labels Nov 5, 2022
@spytheman
Copy link
Member Author

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.

@spytheman
Copy link
Member Author

println(a) should work, just like println(a.str()) does

@spytheman
Copy link
Member Author

println('$a') should work too, just like println(a.str()) does

@spytheman
Copy link
Member Author

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.

@spytheman
Copy link
Member Author

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

@spytheman
Copy link
Member Author

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.

@spytheman
Copy link
Member Author

spytheman commented Nov 13, 2022

The only remaining issue is:
ra := a.as_ref()
is not inferring, that the return type should be Maybe<&int> and NOT Maybe as it does:
dump( typeof(ra).name ) produces: [g.v:142] typeof(ra).name: Maybe<int>, but it should be:
[g.v:142] typeof(ra).name: Maybe<&int>

@MCausc78
Copy link
Contributor

MCausc78 commented Mar 2, 2024

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`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug This tag is applied to issues which reports bugs. Generics[T] Bugs/feature requests, that are related to the V generics. Unit: cgen Bugs/feature requests, that are related to the default C generating backend. Unit: Checker Bugs/feature requests, that are related to the type checker. Unit: Type System Bugs/feature requests, that are related to the V types system.
Projects
None yet
2 participants