Skip to content

Commit

Permalink
Fixed optional on nil value (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
seborama committed Apr 2, 2022
1 parent 7aaedd4 commit b2683ec
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 12 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ Collectors:
- Filtering
- Reducing
- ToSlice
- ToMap*

Check the [godoc](https://pkg.go.dev/github.com/seborama/fuego/v11) for full details.

Expand Down Expand Up @@ -282,14 +283,20 @@ Focus on _**what**_ needs doing in your streams (and delegate the details of the

## [Golang, Receivers and Functions](#golang-receivers-and-functions)

Some tests (e.g. TestCollector_Filtering) are using receiver Getter methods in guise of `Function[T, R any]`. Here is the explanation how this is possible.
Some tests (e.g. `TestCollector_Filtering`) are using receiver Getter methods in guise of `Function[T, R any]`. Here is the explanation how this is possible.

`Function[T, R any]` is defined as `func(T) R`.

A method Getter is typically defined as `func (T) Property() R {...}`.

While `Property()` does not take any argument, Go allows `T.Property` be called as `T.Property(t)`, where `t` is the receiver. This is a `func(T) R` and hence a `Function[T, R any]`.

Example - `TestCollector_Filtering`:

`employee.Department(employees[0])` is the same as `employees[0].Department()`, and of course, they both return a `string`.

The first syntax has one advantage for our purpose though: it is a `Function[T, R any]`.

[(toc)](#table-of-content)

## [Known limitations](#known-limitations)
Expand Down
2 changes: 2 additions & 0 deletions collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ func Reducing[T any](f2 BiFunction[T, T, T]) Collector[T, Optional[T], T] {
}

finisher := func(e Optional[T]) T {
// alternative:
// return e.OrElse(*new(T))
return e.Get()
}

Expand Down
24 changes: 18 additions & 6 deletions optional.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package fuego

import (
"reflect"
)

// Optional is a container object which may or may not contain a value (NO: nil is considered a non-value).
// IMPORTANT NOTE:
// Currently, Go 1.18 does not permit nil generic types.
// See: https://github.com/golang/go/issues/22729
//
// See IsPresent().
//
// Additional methods that depend on the presence or absence of a contained value are provided,
Expand Down Expand Up @@ -105,16 +105,28 @@ func (o Optional[T]) Map(f Function[T, Any]) Optional[Any] {
}

// OptionalOf returns an Optional describing the given (NO: non-nil) value.
// IMPORTANT NOTE:
// Currently, Go 1.18 does not permit nil generic types.
// See: https://github.com/golang/go/issues/22729
func OptionalOf[T any](val T) Optional[T] {
return Optional[T]{
value: val,
present: true,
present: !isNil(val),
}
}

func isNil(v any) bool {
// hat tip to TeaEntityLab/fpGo:
// https://github.com/TeaEntityLab/fpGo/blob/5a35fcbc23e384be5f9b33069e3e3ecc9c661bf4/fp.go#L1218
val := reflect.ValueOf(v)
kind := reflect.ValueOf(v).Kind()

if kind == reflect.Ptr {
return val.IsNil()
}

return !val.IsValid()
}

// OptionalEmpty returns an empty Optional instance. No value is present for this Optional.
func OptionalEmpty[T any]() Optional[T] {
return Optional[T]{
Expand Down
8 changes: 3 additions & 5 deletions optional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ func TestOptional_OptionalOf_ZeroValue_IsPresent_True(t *testing.T) {
assert.True(t, o.IsPresent())
}

func TestOptional_OptionalOf_Nil_Panics(t *testing.T) {
// IMPORTANT NOTE:
// Currently, Go 1.18 does not permit nil generic types.
// See: https://github.com/golang/go/issues/22729
// assert.PanicsWithValue(t, PanicNilNotPermitted, func() { OptionalOf[*string](nil) })
func TestOptional_OptionalOf_Nil_IsPresent_False(t *testing.T) {
o := OptionalOf[*string](nil)
assert.False(t, o.IsPresent())
}

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

0 comments on commit b2683ec

Please sign in to comment.