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

2024-10-22 ยท 3 months 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.

Proxy Information
Original URL
gemini://bbs.geminispace.org/u/greg/21101
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
86.095572 milliseconds
Gemini-to-HTML Time
0.900131 milliseconds

This content has been proxied by September (3851b).