Skip to content

Commit

Permalink
Merge pull request #18 from moznion/from_nillable
Browse files Browse the repository at this point in the history
Support two value factory methods: `FromNillable()` and `PtrFromNillable()`
  • Loading branch information
moznion committed Nov 12, 2022
2 parents c8062ba + 5e74428 commit 610af3c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 6 deletions.
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

0 comments on commit 610af3c

Please sign in to comment.