Documentation
¶
Overview ¶
Package ncaction is basically action package without context.Context support.
Example ¶
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package main
import (
"database/sql"
"errors"
)
// read value from remote api, can be canceled by context.
func readData() (int, error) {
// should add logging and error processing here
return 1, nil
}
// create db connection.
func connectDB(dsn string) (*sql.DB, error) {
// should add logging and error processing here
_ = dsn
return nil, errors.New("fake")
}
// close db
func closeDB(db *sql.DB) error {
// should add logging and error processing here
return db.Close()
}
// functions accepting [Data] is not recommended as it introduces extra complexity,
// but performance is barely improved.
func fasterCloseDB(db Data[*sql.DB]) func() {
return func() {
if conn, err := db(); err == nil {
conn.Close()
}
}
}
// save val to db, can be canceled by context.
func saveToDB(db *sql.DB, val int) error {
// should add logging and error processing here
_, _ = db, val
return errors.New("fake")
}
func main() {
const dsn = "my db dsn"
// Traditional approach
func() {
db, err := connectDB(dsn)
if err != nil {
return
}
defer db.Close() // should add error processing
data, err := readData()
if err != nil {
return
}
err = saveToDB(db, data)
if err != nil {
return
}
// what if you want to retry db connecting if failed?
}()
// Using this package, shared func should be renamed to be more descriptive
//
// Pros:
// - less if err != nil block
// - descriptive
// - easier to write testable code
// - useful helper to do common jobs like retrying
//
// Cons:
// - hard to trace
// - slower (can be ignored when doing IO task)
// - use more resource sometimes
// - need to cache the result sometimes, which uses extra sync.Once
func() {
db := Get(connectDB).By(dsn).Cached()
// to retry connecting, use this line instead
// db := Get(connectDB).By(dsn).RetryN(3).Cached()
err := Do2(saveToDB).
Use(db).
Use(readData).
Defer(Do(closeDB).Use(db).NoErr()).
// can be a little bit faster, but not worthing it
// Defer(fasterCloseDB(db)).
Run()
if err != nil {
return
}
}()
}
Output:
Index ¶
- func Task[T any](_ T) error
- type Action
- func (a Action[T]) AlterError(f func(error) error) Action[T]
- func (a Action[T]) Apply(v T) nctask.Task
- func (a Action[T]) Defer(f func()) Action[T]
- func (a Action[T]) NoErr() func(T)
- func (a Action[T]) Post(f func(T, error)) Action[T]
- func (a Action[T]) Pre(f func(v T)) Action[T]
- func (act Action[T]) Then(next Action[T]) Action[T]
- func (a Action[T]) Use(d Data[T]) nctask.Task
- type Action2
- type Action3
- type Action4
- type Converter
- func (c Converter[I, O]) AlterError(f func(error) error) Converter[I, O]
- func (c Converter[I, O]) AlterOutput(f func(O, error) (O, error)) Converter[I, O]
- func (c Converter[I, O]) By(i I) Data[O]
- func (c Converter[I, O]) Defer(f func()) Converter[I, O]
- func (c Converter[I, O]) From(i Data[I]) Data[O]
- func (c Converter[I, O]) Post(f func(I, O, error)) Converter[I, O]
- func (c Converter[I, O]) Pre(f func(I)) Converter[I, O]
- func (c Converter[I, O]) Then(next Converter[O, O]) Converter[I, O]
- type Converter2
- type Converter3
- type Converter4
- type Data
- func (d Data[T]) AlterError(f func(error) error) Data[T]
- func (d Data[T]) AlterOutput(f func(T, error) (T, error)) Data[T]
- func (d Data[T]) Cached() Data[T]
- func (d Data[T]) Default(v T) Data[T]
- func (d Data[T]) DefaultIf(errf func(error) bool, v T) Data[T]
- func (d Data[T]) DefaultIfNot(errf func(error) bool, v T) Data[T]
- func (d Data[T]) Defer(f func()) Data[T]
- func (d Data[T]) Do(a Action[T]) nctask.Task
- func (d Data[T]) Get() (T, error)
- func (d Data[T]) NoErr() func() T
- func (d Data[T]) Post(f func(T, error)) Data[T]
- func (d Data[T]) Pre(f func()) Data[T]
- func (d Data[T]) Retry() Data[T]
- func (d Data[T]) RetryIf(errf func(error) bool) Data[T]
- func (d Data[T]) RetryN(n int) Data[T]
- func (d Data[T]) RetryNIf(n int, errf func(error) bool) Data[T]
- func (d Data[T]) Saved() Data[T]
- func (d Data[T]) Then(c Converter[T, T]) Data[T]
- func (d Data[T]) Timed(dur time.Duration) Data[T]
- func (d Data[T]) TimedDone(dur time.Duration) Data[T]
- func (d Data[T]) TimedDoneF(f func(time.Duration) time.Duration) Data[T]
- func (d Data[T]) TimedF(f func(time.Duration) time.Duration) Data[T]
- func (d Data[T]) TimedFail(dur time.Duration) Data[T]
- func (d Data[T]) TimedFailF(f func(time.Duration) time.Duration) Data[T]
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Task ¶
Task is a predefined Action used to convert Data into nctask.Task like
err := Get(saveToDB).By(dbConn).From(myData).To(Task).Run(ctx)
Types ¶
type Action ¶
Action is a specialized nctask.Task which accepts one param.
It is designed to interact with the value generated by Data to do something. An nctask.Task is created after you bind action and data together. It is suggested to name it using short phrase to describe what will it do, like "saveToDB", to write code like this:
Use(resp).To(saveToDB) Do(saveToDB).Use(resp)
Both means if value is generated by resp successfully, save it to DB and return any error.
func (Action[T]) AlterError ¶
AlterError wraps a to convert error before return it.
func (Action[T]) Apply ¶
Apply creates a nctask.Task that executes the action with a value.
func (Action[T]) NoErr ¶
func (a Action[T]) NoErr() func(T)
NoErr converts Action into simple function that ignores error.
type Action2 ¶
Action2 is an action that accepts two parameters.
type Action3 ¶
Action3 is like Action2, but accepts one more param.
type Action4 ¶
Action4 is like Action3, but accepts one more param.
type Converter ¶
Converter is a function to convert one type of data into another.
It is designed to interact with the value generated by Data to create a new value. In other words, it creates a new Data from one Data. It is suggested to name it using short phrase to describe what new data is, like "response", to write code like this:
Get(response).From(request)
which means creates a response by do something to request and returns any error.
func Join ¶
Join creates a new Converter by joining two converters.
It's impossible to implement something like Join(i, j, k, ...) because of language design.
func (Converter[I, O]) AlterError ¶
AlterError creates a new Converter by modifying the error with f.
func (Converter[I, O]) AlterOutput ¶
AlterOutput creates a new Converter by modifying the output with f.
type Converter2 ¶
Converter2 is an Converter that accepts two input.
func Get2 ¶
func Get2[A, B, O any](f func(A, B) (O, error)) Converter2[A, B, O]
Get2 creates an Converter2, mostly for type converting purpose.
func NoErrGet2 ¶
func NoErrGet2[A, B, O any](f func(A, B) O) Converter2[A, B, O]
NoErrGet2 is "NoErr" version of Get2.
func (Converter2[A, B, O]) By ¶
func (c Converter2[A, B, O]) By(va A) Converter[B, O]
By creates a Converter by currifying c with a value.
type Converter3 ¶
Converter3 is an Converter2 with additional input.
func Get3 ¶
func Get3[A, B, C, O any](f func(A, B, C) (O, error)) Converter3[A, B, C, O]
Get3 creates an Converter3, mostly for type converting purpose.
func NoErrGet3 ¶
func NoErrGet3[A, B, C, O any](f func(A, B, C) O) Converter3[A, B, C, O]
NoErrGet3 is "NoErr" version of Get3.
func (Converter3[A, B, C, O]) By ¶
func (c Converter3[A, B, C, O]) By(va A) Converter2[B, C, O]
By creates a Converter2 by currifying c with a value.
func (Converter3[A, B, C, O]) From ¶
func (c Converter3[A, B, C, O]) From(a Data[A]) Converter2[B, C, O]
From creates a Converter2 by currifying c with a Data.
type Converter4 ¶
Converter4 is an Converter3 with additional input.
func Get4 ¶
func Get4[A, B, C, D, O any](f func(A, B, C, D) (O, error)) Converter4[A, B, C, D, O]
Get4 creates an Converter4, mostly for type converting purpose.
func NoErrGet4 ¶
func NoErrGet4[A, B, C, D, O any](f func(A, B, C, D) O) Converter4[A, B, C, D, O]
NoErrGet4 is "NoErr" version of Get4.
func (Converter4[A, B, C, D, O]) By ¶
func (c Converter4[A, B, C, D, O]) By(va A) Converter3[B, C, D, O]
By creates a Converter3 by currifying c with a value.
func (Converter4[A, B, C, D, O]) From ¶
func (c Converter4[A, B, C, D, O]) From(a Data[A]) Converter3[B, C, D, O]
From creates a Converter3 by currifying c with a Data.
type Data ¶
Data is a function which can generate some data.
Though it is named "data", it's more like a factory or "promise in JS". It is used to generate value to be used with Action or Converter. Take a look at their document for more info.
func Future ¶
Future creates a cached Data, whose result is determined by a function. Getting value of the Data will be blocked until the result is determined.
func (Data[T]) AlterError ¶
AlterError wraps d to run f on its error.
func (Data[T]) AlterOutput ¶
AlterOutput wraps d to run f on its output.
func (Data[T]) Cached ¶
Cached wraps d to cache its result, no matter success or failed.
Example ¶
a := Use(func() (int32, error) {
fmt.Println("executed")
ret := rand.Int31n(100)
if ret < 50 {
return 0, errors.New("50/50")
}
return ret, nil
}).Cached()
v1, e1 := a() // print "executed"
v2, e2 := a() // nothing printed
if v1 != v2 {
fmt.Println("v1 != v2")
}
if e1 != e2 {
fmt.Println("e1 != e2")
}
Output: executed
func (Data[T]) DefaultIf ¶
DefaultIf wraps d to return v instead if any error matched by errf occurred.
func (Data[T]) DefaultIfNot ¶
DefaultIfNot uses v if error occurred and is NOT matched by errf.
func (Data[T]) Do ¶
Do creates a nctask.Task by doing something with its value.
func (Data[T]) NoErr ¶
func (d Data[T]) NoErr() func() T
NoErr converts Data into simple function that ignores error.
func (Data[T]) RetryIf ¶
RetryIf wraps d to run it repeatly until success or errf returns false.
Error passed to errf will never be nil.
func (Data[T]) RetryN ¶
RetryN is like Retry, but no more than n times.
RetryN(3) will run at most 4 times, first attempt is not considered as retrying.
func (Data[T]) RetryNIf ¶
RetryNIf is like RetryIf, but no more than n times.
Error passed to errf will never be nil.
func (Data[T]) Saved ¶
Saved wraps d to cache its result only when success.
Example ¶
err := errors.New("error")
cnt := 0
a := Use(func() (int, error) {
fmt.Println("executed")
cnt++
if cnt < 2 {
return cnt, err
}
return cnt, nil
}).Saved()
fmt.Println(a())
fmt.Println(a())
fmt.Println(a())
Output: executed 1 error executed 2 <nil> 2 <nil>
func (Data[T]) Timed ¶
Timed wraps d into a Data and ensures that it is not returned before dur passed.
It focuses on "How long I should wait before returning". Take a look at example of [task.Task.Timed] for how it works.
func (Data[T]) TimedDoneF ¶
TimedDoneF is like TimedDone, but use function instead.
The function accepts actual execution time, and returns how long it should wait.
func (Data[T]) TimedF ¶
TimedF is like Timed, but use function instead.
The function accepts actual execution time, and returns how long it should wait.