type Optional[T any] struct {
value *T
}
Don't use a pointer - it's bad for performance. func MakeOptional[T any](value *T) Optional[T] {
Use `New` instead - it's the idiomatic name for a constructor in Go. Drop the `Optional` part - the module name suffices - the caller will see: `opt.New`. Personally I just call the module `optional`, I think it's clearer: `optional.New`. func (o* Optional[T]) Unwrap() (T, error) {
1. `Unwrap` reminds me of Rust's .Unwrap, which panics. Seems confusing.2. There's no error here, the situation is equivalent to a missing key in a map - it's enough to return a bool.
Here's my implementation so far: https://gist.github.com/Mawr-BF2/0a60da26f66b82ee87b98b03336.... Only thing I'm not sure about is whether the JSON serialization methods are necessary.
I agree with peer comment that we should likely defer to the original type for JSON serialization.
Where do you get this from? I've tried unsuccessfully to find such naming/design guidelines.
[1]: https://github.com/golang/go/commit/85e7bee19f9f26dfca414b1e...
Pointers to pointers are generally something you try to avoid if possible.
But any benefits of the packing are gone whether I make a pointer or not.
And either way, no nested pointers are involved. We’re not taking pointers to options in either case.
to use a tired example: when getting a value out of a map via some `myMap.get(key)`, you may want to distinguish "not present" = `None` and "present, with value null" = `Some(null)`
the right solution is to just not have nulls in the first place, then there's no problem ;)
Some(null) is not a valid result.
The whole point of the Option type is to let you know:
a) we got a result: Some(value)
b) there is no result: None
struct Foo;
fn main() {
let option_with_null: Option<*const Foo> = Some(std::ptr::null());
dbg!(option_with_null);
dbg!(option_with_null.is_none());
}
Output: [src/main.rs:6] option_with_null = Some(
0x0000000000000000,
)
[src/main.rs:7] option_with_null.is_none() = false
https://play.rust-lang.org/?version=stable&mode=debug&editio...> Some(null) is not a valid result.
It is a valid result. Null pointer and Option::None are still different things, is all.
That means you must have Optional[*sync.Mutex]. Then what's the point?
func hello() (string, bool)The problem becomes clearer once you venture out of this exact case. How would you embed this pattern in a struct?
Like so?
type A struct {
value string
valueOK bool
}
Or maybe with a pointer? type B struct {
value *string
}
This option is the most common one in Go code today.You can perhaps see the issues already - these are awkward, neither properly communicates that what we want is optionality.
But the real issue is that now that these are in a struct, nothing prevents us from accessing the value without checking if it's valid first:
a := A{value: "", valueOk: false}
// oops
fmt.Println(a.value)
a := B{value: nil}
// oops
fmt.Println(a.value)
This wasn't an issue in your example - if we attempt to only access the value, and omit assigning the bool, the code will not compile: func hello() (string, bool) {
return "hi", false
}
func main() {
value := hello()
// ^^^ compiler error: assignment mismatch: 1 variable but hello returns 2 values ([1])
fmt.Println(value)
}
An Optional type fixes this by not allowing you to access the value directly, but to instead have to go through a function like the one above.I've used optionals in many languages, and this is the first I've heard of it. What else do you really need except map and... flatMap (aka `then` aka `and_then` aka `>>=` aka `bind`).
rough analogy: `await` replaces `.then()`, do-notation replaces `.flatMap()`
To be more precise, it was when .NET 3.5 was new (2008). That brought a lot of visibility to Nullable<T> because it was featured prominently in LINQ. But ?. didn't come until C# 6 (2014). I was wrong about ??, though - that one was added along with Nullable itself.