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

Support two value factory methods: FromNillable() and PtrFromNillable() #18

Merged
merged 1 commit into from
Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ and more detailed examples are here: [./examples_test.go](./examples_test.go).

### Supported Operations

#### Value Factory Methods

- [Some[T]() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#Some)
- [None[T]() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#None)
- [FromNillable[T]() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#FromNillable)
- [PtrFromNillable[T]() Option[T]](https://pkg.go.dev/github.com/moznion/go-optional#PtrFromNillable)

#### Option value handler methods

- [Option[T]#IsNone() bool](https://pkg.go.dev/github.com/moznion/go-optional#Option.IsNone)
- [Option[T]#IsSome() bool](https://pkg.go.dev/github.com/moznion/go-optional#Option.IsSome)
- [Option[T]#Unwrap() T](https://pkg.go.dev/github.com/moznion/go-optional#Option.Unwrap)
Expand All @@ -69,6 +78,16 @@ and more detailed examples are here: [./examples_test.go](./examples_test.go).
- [Option.Unzip[T, U any](zipped Option[Pair[T, U]]) (Option[T], Option[U])](https://pkg.go.dev/github.com/moznion/go-optional#Unzip)
- [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)

### nil == None[T]

This library deals with `nil` as same as `None[T]`. So it works with like the following example:

```go
var nilValue Option[int] = nil
fmt.Printf("%v\n", nilValue.IsNone()) // => true
fmt.Printf("%v\n", nilValue.IsSome()) // => false
```

### JSON marshal/unmarshal support

This `Option[T]` type supports JSON marshal and unmarshal.
Expand Down Expand Up @@ -147,10 +166,6 @@ if err != nil {
fmt.Printf("%s\n", marshal) // => {}
```

## Tips

- it would be better to deal with an Option value as a non-pointer because if the Option value can accept nil it becomes worthless

## Known Issues

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.
Expand Down
50 changes: 50 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,79 @@ func ExampleOption_IsNone() {
fmt.Printf("%v\n", some.IsNone())
none := None[int]()
fmt.Printf("%v\n", none.IsNone())

num := 123
some = FromNillable[int](&num)
fmt.Printf("%v\n", some.IsNone())
none = FromNillable[int](nil)
fmt.Printf("%v\n", none.IsNone())

ptrSome := PtrFromNillable[int](&num)
fmt.Printf("%v\n", ptrSome.IsNone())
ptrNone := PtrFromNillable[int](nil)
fmt.Printf("%v\n", ptrNone.IsNone())

var nilValue Option[int] = nil
fmt.Printf("%v\n", nilValue.IsNone())

// Output:
// false
// true
// false
// true
// false
// true
// true
}

func ExampleOption_IsSome() {
some := Some[int](1)
fmt.Printf("%v\n", some.IsSome())
none := None[int]()
fmt.Printf("%v\n", none.IsSome())

num := 123
some = FromNillable[int](&num)
fmt.Printf("%v\n", some.IsSome())
none = FromNillable[int](nil)
fmt.Printf("%v\n", none.IsSome())

ptrSome := PtrFromNillable[int](&num)
fmt.Printf("%v\n", ptrSome.IsSome())
ptrNone := PtrFromNillable[int](nil)
fmt.Printf("%v\n", ptrNone.IsSome())

var nilValue Option[int] = nil
fmt.Printf("%v\n", nilValue.IsSome())

// Output:
// true
// false
// true
// false
// true
// false
// false
}

func ExampleOption_Unwrap() {
fmt.Printf("%v\n", Some[int](12345).Unwrap())
fmt.Printf("%v\n", None[int]().Unwrap())
fmt.Printf("%v\n", None[*int]().Unwrap())

num := 123
fmt.Printf("%v\n", FromNillable[int](&num).Unwrap())
fmt.Printf("%v\n", FromNillable[int](nil).Unwrap())
fmt.Printf("%v\n", *PtrFromNillable[int](&num).Unwrap()) // NOTE: this dereferences tha unwrapped value
fmt.Printf("%v\n", PtrFromNillable[int](nil).Unwrap())
// Output:
// 12345
// 0
// <nil>
// 123
// 0
// 123
// <nil>
}

func ExampleOption_Take() {
Expand Down
25 changes: 23 additions & 2 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,39 @@ const (
value = iota
)

// Some is a function to make an Option type instance with the actual value.
// Some is a function to make an Option type value with the actual value.
func Some[T any](v T) Option[T] {
return Option[T]{
value: v,
}
}

// None is a function to make an Option type that doesn't have a value.
// None is a function to make an Option type value that doesn't have a value.
func None[T any]() Option[T] {
return nil
}

// FromNillable is a function to make an Option type value with the nillable value with value de-referencing.
// If the given value is not nil, this returns Some[T] value. On the other hand, if the value is nil, this returns None[T].
// This function does "dereference" for the value on packing that into Option value. If this value is not preferable, please consider using PtrFromNillable() instead.
func FromNillable[T any](v *T) Option[T] {
if v == nil {
return None[T]()
}
return Some[T](*v)
}

// PtrFromNillable is a function to make an Option type value with the nillable value without value de-referencing.
// If the given value is not nil, this returns Some[*T] value. On the other hand, if the value is nil, this returns None[*T].
// This function doesn't "dereference" the value on packing that into the Option value; in other words, this puts the as-is pointer value into the Option envelope.
// This behavior contrasts with the FromNillable() function's one.
func PtrFromNillable[T any](v *T) Option[*T] {
if v == nil {
return None[*T]()
}
return Some[*T](v)
}

// IsNone returns whether the Option *doesn't* have a value or not.
func (o Option[T]) IsNone() bool {
return o == nil
Expand Down
20 changes: 20 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,37 @@ import (
func TestOption_IsNone(t *testing.T) {
assert.True(t, None[int]().IsNone())
assert.False(t, Some[int](123).IsNone())

var nilValue Option[int] = nil
assert.True(t, nilValue.IsNone())

i := 0
assert.False(t, FromNillable[int](&i).IsNone())
assert.True(t, FromNillable[int](nil).IsNone())
}

func TestOption_IsSome(t *testing.T) {
assert.False(t, None[int]().IsSome())
assert.True(t, Some[int](123).IsSome())

var nilValue Option[int] = nil
assert.False(t, nilValue.IsSome())

i := 0
assert.True(t, FromNillable[int](&i).IsSome())
assert.False(t, FromNillable[int](nil).IsSome())
}

func TestOption_Unwrap(t *testing.T) {
assert.Equal(t, "foo", Some[string]("foo").Unwrap())
assert.Equal(t, "", None[string]().Unwrap())
assert.Nil(t, None[*string]().Unwrap())

i := 123
assert.Equal(t, i, FromNillable[int](&i).Unwrap())
assert.Equal(t, 0, FromNillable[int](nil).Unwrap())
assert.Equal(t, i, *PtrFromNillable[int](&i).Unwrap())
assert.Nil(t, PtrFromNillable[int](nil).Unwrap())
}

func TestOption_Take(t *testing.T) {
Expand Down