๐Ÿ’พ Archived View for bbs.geminispace.org โ€บ u โ€บ greg โ€บ 21101 captured on 2024-12-17 at 15:10:49. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

No, you can't build Optional[T] or Maybe[T] with Go

From time to time, someone shows up on Mastodon, in /r/golang, or on the Gophers Slack to show how they implemented optional values like you see in Haskell, OCaml, TypeScript, or Rust in Go.

All of these approaches have one thing in common: they're not really Optional[T] or Maybe[T].

They look like it on the surface: there are generics involved, they may have a "is this valid?" boolean flag field, the name is right, etc...

What all of these don't have (and can't have, in Go's type system and language structure) is the one thing that gives optional types their power:

There is no way to _enforce_ that a value is set and have that statically checked through the compiler, simply because Go does not have an equivalent to the match operator. All you get is a container type that you _can_ ask whether the value it contains is valid, but noone forces you to actually check that.

The safety guarantees of this are about the same as that of a regular *T: nearly none. You have to explicitly check (and remember to check!) whether the value is valid or not before using it.

One thing these types _do_ give you is that they're an annotation for readers of your code that the zero value of T is likely different from "no value set". So there's that.

#go

๐ŸŒฒ greg

Oct 22 ยท 8 weeks ago ยท ๐Ÿ‘ mediocregopher, StanStani, LucasMW

4 Comments โ†“

๐Ÿ satch ยท Oct 22 at 21:02:

Can you give a little bit of background - explain what Optional[T] and Maybe[T] are?

๐Ÿšฒ halscode ยท Oct 23 at 01:30:

I think Greg already outlined it:

to _enforce_ that a value is set and have that statically checked through the compiler

๐Ÿš€ clseibold ยท Oct 23 at 02:56:

@satch It lets you specify a nil-like value for non-nilable types, basically. You can also do this with empty interfaces in Golang, but you lose the expected type and it isn't that great.

I don't know what this person is on about though - Haskell and Rust didn't invent using generics for Maybes, so it's not like those languages are the arbiters of what a Maybe is, so Golang's version of Maybes are just as valid as Java's and C++'s. They aren't the same as in Haskell and Rust, but that doesn't make them not a Maybe, lol.

The "they're not really a Maybe[T]" thing is just odd. Yes, they are, they just aren't the Maybe[T]s of Rust and Haskell and OCaml. The ones in Golang serve the core purpose of Maybe, which is to specify a nil value for non-nilable types. That's it. Anything else is just extra checking, syntactic sugar, or the addition of things like tagged unions and enums that wrap values that are nice to have, but not actually required.

Comparing a Maybe[T] in golang to a pointer is incorrect, because accidentally accessing an empty value when you wanted a nil value for non-pointers doesn't lead to memory access violations or nearly as many security risks. Also, implying that Golang pointer usage is just as bad as C or C++ would be just as incorrect.

Compile-time pattern matching is a *separate* feature that interacts with other features, like polymorphism. It isn't polymorphism itself. Odin is an example of a language that has *some* comile-time pattern matching for parametric functions, and is very much based off of Golang. It certainly doesn't go to the extent of Haskell or Rust though, of course.

Rust allows for enums to wrap a value, which is how it's used with the match operator. Option<> is defined in Rust as a generic enum with two variants, Some(T) and None.

๐Ÿš€ mk270 ยท Oct 25 at 13:37:

It is adorable that the misunderstandings of statically type-checking sometimes uttered by proponents of dynamically type-checked languages can be reproduced in statically type-checked languages like Go.