github.com/moznion/go-optional@v0.11.1-0.20240312043125-6881072e44c1/README.md (about) 1 # go-optional [![.github/workflows/check.yml](https://github.com/moznion/go-optional/actions/workflows/check.yml/badge.svg)](https://github.com/moznion/go-optional/actions/workflows/check.yml) [![codecov](https://codecov.io/gh/moznion/go-optional/branch/main/graph/badge.svg?token=0HCVy6COy4)](https://codecov.io/gh/moznion/go-optional) [![GoDoc](https://godoc.org/github.com/moznion/go-optional?status.svg)](https://godoc.org/github.com/moznion/go-optional) 2 3 A library that provides [Go Generics](https://go.dev/blog/generics-proposal) friendly "optional" features. 4 5 ## Synopsis 6 7 ```go 8 some := optional.Some[int](123) 9 fmt.Printf("%v\n", some.IsSome()) // => true 10 fmt.Printf("%v\n", some.IsNone()) // => false 11 12 v, err := some.Take() 13 fmt.Printf("err is nil: %v\n", err == nil) // => err is nil: true 14 fmt.Printf("%d\n", v) // => 123 15 16 mapped := optional.Map(some, func (v int) int { 17 return v * 2 18 }) 19 fmt.Printf("%v\n", mapped.IsSome()) // => true 20 21 mappedValue, _ := some.Take() 22 fmt.Printf("%d\n", mappedValue) // => 246 23 ``` 24 25 ```go 26 none := optional.None[int]() 27 fmt.Printf("%v\n", none.IsSome()) // => false 28 fmt.Printf("%v\n", none.IsNone()) // => true 29 30 _, err := none.Take() 31 fmt.Printf("err is nil: %v\n", err == nil) // => err is nil: false 32 // the error must be `ErrNoneValueTaken` 33 34 mapped := optional.Map(none, func (v int) int { 35 return v * 2 36 }) 37 fmt.Printf("%v\n", mapped.IsNone()) // => true 38 ``` 39 40 and more detailed examples are here: [./examples_test.go](./examples_test.go). 41 42 ## Docs 43 44 [![GoDoc](https://godoc.org/github.com/moznion/go-optional?status.svg)](https://godoc.org/github.com/moznion/go-optional) 45 46 ### Supported Operations 47 48 #### Value Factory Methods 49 50 - [Some[T]\() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#Some) 51 - [None[T]\() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#None) 52 - [FromNillable[T]\() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#FromNillable) 53 - [PtrFromNillable[T]\() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#PtrFromNillable) 54 55 #### Option value handler methods 56 57 - [Option[T]#IsNone() bool](https://pkg.go.dev/github.com/moznion/go-optional#Option.IsNone) 58 - [Option[T]#IsSome() bool](https://pkg.go.dev/github.com/moznion/go-optional#Option.IsSome) 59 - [Option[T]#Unwrap() T](https://pkg.go.dev/github.com/moznion/go-optional#Option.Unwrap) 60 - [Option[T]#UnwrapAsPtr() \*T](https://pkg.go.dev/github.com/moznion/go-optional#Option.UnwrapAsPtr) 61 - [Option[T]#Take() (T, error)](https://pkg.go.dev/github.com/moznion/go-optional#Option.Take) 62 - [Option[T]#TakeOr(fallbackValue T) T](https://pkg.go.dev/github.com/moznion/go-optional#Option.TakeOr) 63 - [Option[T]#TakeOrElse(fallbackFunc func() T) T](https://pkg.go.dev/github.com/moznion/go-optional#Option.TakeOrElse) 64 - [Option[T]#Or(fallbackOptionValue Option[T]) Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#Option.Or) 65 - [Option[T]#OrElse(fallbackOptionFunc func() Option[T]) Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#Option.OrElse) 66 - [Option[T]#Filter(predicate func(v T) bool) Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#Option.Filter) 67 - [Option[T]#IfSome(f func(v T))](https://pkg.go.dev/github.com/moznion/go-optional#Option.IfSome) 68 - [Option[T]#IfSomeWithError(f func(v T) error) error](https://pkg.go.dev/github.com/moznion/go-optional#Option.IfSomeWithError) 69 - [Option[T]#IfNone(f func())](https://pkg.go.dev/github.com/moznion/go-optional#Option.IfNone) 70 - [Option[T]#IfNoneWithError(f func() error) error](https://pkg.go.dev/github.com/moznion/go-optional#Option.IfNoneWithError) 71 - [Option.Map[T, U any](option Option[T], mapper func(v T) U) Option[U]](https://pkg.go.dev/github.com/moznion/go-optional#Map) 72 - [Option.MapOr[T, U any](option Option[T], fallbackValue U, mapper func(v T) U) U](https://pkg.go.dev/github.com/moznion/go-optional#MapOr) 73 - [Option.MapWithError[T, U any](option Option[T], mapper func(v T) (U, error)) (Option[U], error)](https://pkg.go.dev/github.com/moznion/go-optional#MapWithError) 74 - [Option.MapOrWithError[T, U any](option Option[T], fallbackValue U, mapper func(v T) (U, error)) (U, error)](https://pkg.go.dev/github.com/moznion/go-optional#MapOrWithError) 75 - [Option.FlatMap[T, U any](option Option[T], mapper func(v T) Option[U]) Option[U]](https://pkg.go.dev/github.com/moznion/go-optional#FlatMap) 76 - [Option.FlatMapOr[T, U any](option Option[T], fallbackValue U, mapper func(v T) Option[U]) U](https://pkg.go.dev/github.com/moznion/go-optional#FlatMapOr) 77 - [Option.FlatMapWithError[T, U any](option Option[T], mapper func(v T) (Option[U], error)) (Option[U], error)](https://pkg.go.dev/github.com/moznion/go-optional#FlatMapWithError) 78 - [Option.FlatMapOrWithError[T, U any](option Option[T], fallbackValue U, mapper func(v T) (Option[U], error)) (U, error)](https://pkg.go.dev/github.com/moznion/go-optional#FlatMapOrWithError) 79 - [Option.Zip[T, U any](opt1 Option[T], opt2 Option[U]) Option[Pair[T, U]]](https://pkg.go.dev/github.com/moznion/go-optional#Zip) 80 - [Option.ZipWith[T, U, V any](opt1 Option[T], opt2 Option[U], zipper func(opt1 T, opt2 U) V) Option[V]](https://pkg.go.dev/github.com/moznion/go-optional#ZipWith) 81 - [Option.Unzip[T, U any](zipped Option[Pair[T, U]]) (Option[T], Option[U])](https://pkg.go.dev/github.com/moznion/go-optional#Unzip) 82 - [Option.UnzipWith[T, U, V any](zipped Option[V], unzipper func(zipped V) (T, U)) (Option[T], Option[U])](https://pkg.go.dev/github.com/moznion/go-optional#UnzipWith) 83 84 ### nil == None[T] 85 86 This library deals with `nil` as same as `None[T]`. So it works with like the following example: 87 88 ```go 89 var nilValue Option[int] = nil 90 fmt.Printf("%v\n", nilValue.IsNone()) // => true 91 fmt.Printf("%v\n", nilValue.IsSome()) // => false 92 ``` 93 94 ### JSON marshal/unmarshal support 95 96 This `Option[T]` type supports JSON marshal and unmarshal. 97 98 If the value wanted to marshal is `Some[T]` then it marshals that value into the JSON bytes simply, and in unmarshaling, if the given JSON string/bytes has the actual value on corresponded property, it unmarshals that value into `Some[T]` value. 99 100 example: 101 102 ```go 103 type JSONStruct struct { 104 Val Option[int] `json:"val"` 105 } 106 107 some := Some[int](123) 108 jsonStruct := &JSONStruct{Val: some} 109 110 marshal, err := json.Marshal(jsonStruct) 111 if err != nil { 112 return err 113 } 114 fmt.Printf("%s\n", marshal) // => {"val":123} 115 116 var unmarshalJSONStruct JSONStruct 117 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 118 if err != nil { 119 return err 120 } 121 // unmarshalJSONStruct.Val == Some[int](123) 122 ``` 123 124 Elsewise, when the value is `None[T]`, the marshaller serializes that value as `null`. And if the unmarshaller gets the JSON `null` value on a property corresponding to the `Optional[T]` value, or the value of a property is missing, that deserializes that value as `None[T]`. 125 126 example: 127 128 ```go 129 type JSONStruct struct { 130 Val Option[int] `json:"val"` 131 } 132 133 none := None[int]() 134 jsonStruct := &JSONStruct{Val: none} 135 136 marshal, err := json.Marshal(jsonStruct) 137 if err != nil { 138 return err 139 } 140 fmt.Printf("%s\n", marshal) // => {"val":null} 141 142 var unmarshalJSONStruct JSONStruct 143 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 144 if err != nil { 145 return err 146 } 147 // unmarshalJSONStruct.Val == None[int]() 148 ``` 149 150 And this also supports `omitempty` option for JSON unmarshaling. If the value of the property is `None[T]` and that property has `omitempty` option, it omits that property. 151 152 ref: 153 154 > The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string. 155 > https://pkg.go.dev/encoding/json#Marshal 156 157 example: 158 159 ```go 160 type JSONStruct struct { 161 OmitemptyVal Option[string] `json:"omitemptyVal,omitempty"` // this should be omitted 162 } 163 164 jsonStruct := &JSONStruct{OmitemptyVal: None[string]()} 165 marshal, err := json.Marshal(jsonStruct) 166 if err != nil { 167 return err 168 } 169 fmt.Printf("%s\n", marshal) // => {} 170 ``` 171 172 ### SQL Driver Support 173 174 `Option[T]` satisfies [sql/driver.Valuer](https://pkg.go.dev/database/sql/driver#Valuer) and [sql.Scanner](https://pkg.go.dev/database/sql#Scanner), so this type can be used by SQL interface on Golang. 175 176 example of the primitive usage: 177 178 ```go 179 sqlStmt := "CREATE TABLE tbl (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(32));" 180 db.Exec(sqlStmt) 181 182 tx, _ := db.Begin() 183 func() { 184 stmt, _ := tx.Prepare("INSERT INTO tbl(id, name) values(?, ?)") 185 defer stmt.Close() 186 stmt.Exec(1, "foo") 187 }() 188 func() { 189 stmt, _ := tx.Prepare("INSERT INTO tbl(id) values(?)") 190 defer stmt.Close() 191 stmt.Exec(2) // name is NULL 192 }() 193 tx.Commit() 194 195 var maybeName Option[string] 196 197 row := db.QueryRow("SELECT name FROM tbl WHERE id = 1") 198 row.Scan(&maybeName) 199 fmt.Println(maybeName) // Some[foo] 200 201 row := db.QueryRow("SELECT name FROM tbl WHERE id = 2") 202 row.Scan(&maybeName) 203 fmt.Println(maybeName) // None[] 204 ``` 205 206 ## Known Issues 207 208 The runtime raises a compile error like "methods cannot have type parameters", so `Map()`, `MapOr()`, `MapWithError()`, `MapOrWithError()`, `Zip()`, `ZipWith()`, `Unzip()` and `UnzipWith()` have been providing as functions. Basically, it would be better to provide them as the methods, but currently, it compromises with the limitation. 209 210 ## Author 211 212 moznion (<moznion@mail.moznion.net>) 213