Skip to content

Commit b2683ec

Browse files
authored
Fixed optional on nil value (#23)
1 parent 7aaedd4 commit b2683ec

File tree

4 files changed

+31
-12
lines changed

4 files changed

+31
-12
lines changed

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ Collectors:
235235
- Filtering
236236
- Reducing
237237
- ToSlice
238+
- ToMap*
238239

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

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

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

285-
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.
286+
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.
286287

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

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

291292
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]`.
292293

294+
Example - `TestCollector_Filtering`:
295+
296+
`employee.Department(employees[0])` is the same as `employees[0].Department()`, and of course, they both return a `string`.
297+
298+
The first syntax has one advantage for our purpose though: it is a `Function[T, R any]`.
299+
293300
[(toc)](#table-of-content)
294301

295302
## [Known limitations](#known-limitations)

collector.go

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ func Reducing[T any](f2 BiFunction[T, T, T]) Collector[T, Optional[T], T] {
162162
}
163163

164164
finisher := func(e Optional[T]) T {
165+
// alternative:
166+
// return e.OrElse(*new(T))
165167
return e.Get()
166168
}
167169

optional.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package fuego
22

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

107107
// OptionalOf returns an Optional describing the given (NO: non-nil) value.
108-
// IMPORTANT NOTE:
109108
// Currently, Go 1.18 does not permit nil generic types.
110109
// See: https://github.com/golang/go/issues/22729
111110
func OptionalOf[T any](val T) Optional[T] {
112111
return Optional[T]{
113112
value: val,
114-
present: true,
113+
present: !isNil(val),
115114
}
116115
}
117116

117+
func isNil(v any) bool {
118+
// hat tip to TeaEntityLab/fpGo:
119+
// https://github.com/TeaEntityLab/fpGo/blob/5a35fcbc23e384be5f9b33069e3e3ecc9c661bf4/fp.go#L1218
120+
val := reflect.ValueOf(v)
121+
kind := reflect.ValueOf(v).Kind()
122+
123+
if kind == reflect.Ptr {
124+
return val.IsNil()
125+
}
126+
127+
return !val.IsValid()
128+
}
129+
118130
// OptionalEmpty returns an empty Optional instance. No value is present for this Optional.
119131
func OptionalEmpty[T any]() Optional[T] {
120132
return Optional[T]{

optional_test.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ func TestOptional_OptionalOf_ZeroValue_IsPresent_True(t *testing.T) {
1616
assert.True(t, o.IsPresent())
1717
}
1818

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

2624
func TestOptional_OptionalEmpty_IsPresent_False(t *testing.T) {

0 commit comments

Comments
 (0)