A zero dependency type action (typact) library for GoLang.
WARNING: This library has not reached v1 yet!
go get go.l0nax.org/typact
Examples can be found in the examples
directory or at at the official Godoc page.
Unstable
!
Because of some limitations of the GoLang type system, there are some important facts to remember when
using the Clone
method.
The best way to implement the Clone
method is by using a pointer receiver like this:
type MyData struct {
ID int
CreatedAt time.Time
UpdatedAt time.Time
}
// Clone implements the std.Cloner interface.
func (m *MyData) Clone() *MyData {
return &MyData{
ID: m.ID,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
This allows you to use your type with and without a pointer. For example:
var asPtr typact.Option[*MyData]
var asVal typact.Option[MyData]
Calling Clone
on asPtr
and asVal
will result in a valid clone. This is because the Clone
implementation of the Option[T]
type checks if a type implements the std.Cloner
interface with a
pointer receiver.
For now it is not possible to implement the Clone
method without a pointer receiver and use Clone
on a pointer value:
type MyData struct {
ID int
CreatedAt time.Time
UpdatedAt time.Time
}
// Clone implements the std.Cloner interface.
func (m MyData) Clone() MyData {
return MyData{
ID: m.ID,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
func main() {
data := typact.Some(&MyData{
ID: 15,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
})
data.Clone() // This will panic because *MyData does not implement std.Cloner.
}
Thus the best way to support all use-cases is to implement the interface with a pointer receiver.
Benchmarking has also shown that implementing std.Cloner[T]
with a pointer receiver results in better performance for
all use cases:
Benchmark Results
goos: linux
goarch: amd64
pkg: go.l0nax.org/typact
cpu: AMD Ryzen 9 5900X 12-Core Processor
│ /tmp/base │
│ sec/op │
Option_Clone/None-24 1.915n ± 1%
Option_Clone/String-24 2.461n ± 1%
Option_Clone/Int64-24 2.465n ± 1%
Option_Clone/CustomStructPointer-24 64.79n ± 3%
Option_Clone/CustomStructFallback-24 168.2n ± 9%
Option_Clone/ScalarSlice-24 214.4n ± 4%
Option_Clone/PtrSliceWrapper_PtrRecv-24 1.537µ ± 4%
Option_Clone/SliceWrapper_PtrRecv-24 1.588µ ± 5%
Option_Clone/PtrSliceWrapper_NormalRecv-24 1.943µ ± 5%
Option_Clone/SliceWrapper_NormalRecv-24 1.887µ ± 15%
geomean 109.3n
│ /tmp/base │
│ B/op │
Option_Clone/None-24 0.000 ± 0%
Option_Clone/String-24 0.000 ± 0%
Option_Clone/Int64-24 0.000 ± 0%
Option_Clone/CustomStructPointer-24 48.00 ± 0%
Option_Clone/CustomStructFallback-24 96.00 ± 0%
Option_Clone/ScalarSlice-24 112.0 ± 0%
Option_Clone/PtrSliceWrapper_PtrRecv-24 328.0 ± 0%
Option_Clone/SliceWrapper_PtrRecv-24 408.0 ± 0%
Option_Clone/PtrSliceWrapper_NormalRecv-24 424.0 ± 0%
Option_Clone/SliceWrapper_NormalRecv-24 408.0 ± 0%
geomean ¹
¹ summaries must be >0 to compute geomean
│ /tmp/base │
│ allocs/op │
Option_Clone/None-24 0.000 ± 0%
Option_Clone/String-24 0.000 ± 0%
Option_Clone/Int64-24 0.000 ± 0%
Option_Clone/CustomStructPointer-24 1.000 ± 0%
Option_Clone/CustomStructFallback-24 2.000 ± 0%
Option_Clone/ScalarSlice-24 3.000 ± 0%
Option_Clone/PtrSliceWrapper_PtrRecv-24 11.00 ± 0%
Option_Clone/SliceWrapper_PtrRecv-24 11.00 ± 0%
Option_Clone/PtrSliceWrapper_NormalRecv-24 13.00 ± 0%
Option_Clone/SliceWrapper_NormalRecv-24 11.00 ± 0%
geomean ¹
¹ summaries must be >0 to compute geomean
I've created this library because for one option types are really useful and prevent the one billion dollar mistake with dereferencing nil pointers (at least it reduces the risk).
At my work and within my private projects I often find myself in the position where
- values may be provided (by the programmer, i.e. options)
- values may be
NULL
(i.e. in a database/ JSON/ ...)
Whilst writing my own LSM implementation I frequently ran into the situation where specific checks
are much easier to read when using a declarative approach (see the cmpop.Ordering
type).
Thus this library is not a Option type only library.
The project is licensed under the MIT License.