1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-19 23:42:05 +02:00
Files
fp-go/v2/tailrec/doc.go

215 lines
6.6 KiB
Go
Raw Normal View History

// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package tailrec provides a trampoline implementation for tail-call optimization in Go.
//
// # Overview
//
// Go does not support tail-call optimization (TCO) at the language level, which means
// deeply recursive functions can cause stack overflow errors. The trampoline pattern
// provides a way to convert recursive algorithms into iterative ones, avoiding stack
// overflow while maintaining the clarity of recursive code.
//
// A trampoline works by returning instructions about what to do next instead of
// directly making recursive calls. The trampoline executor then interprets these
// instructions in a loop, effectively converting recursion into iteration.
//
// # Core Concepts
//
// The package provides three main operations:
//
// **Bounce**: Indicates that the computation should continue with a new value.
// This represents a recursive call in the original algorithm.
//
// **Land**: Indicates that the computation is complete and returns a final result.
// This represents the base case in the original algorithm.
//
// **Unwrap**: Extracts the state from a Trampoline, allowing the executor to
// determine whether to continue (Bounce) or terminate (Land).
//
// # Type Parameters
//
// The Trampoline type has two type parameters:
//
// - B: The "bounce" type - the intermediate state passed between recursive steps
// - L: The "land" type - the final result type when computation completes
//
// # Basic Usage
//
// Converting a recursive factorial function to use trampolines:
//
// // Traditional recursive factorial (can overflow stack)
// func factorial(n int) int {
// if n <= 1 {
// return 1
// }
// return n * factorial(n-1)
// }
//
// // Trampoline-based factorial (stack-safe)
// type State struct {
// n int
// acc int
// }
//
// func factorialStep(state State) tailrec.Trampoline[State, int] {
// if state.n <= 1 {
// return tailrec.Land[State](state.acc)
// }
// return tailrec.Bounce[int](State{state.n - 1, state.acc * state.n})
// }
//
// // Execute the trampoline
// func factorial(n int) int {
// current := tailrec.Bounce[int](State{n, 1})
// for {
// bounce, land, isLand := tailrec.Unwrap(current)
// if isLand {
// return land
// }
// current = factorialStep(bounce)
// }
// }
//
// # Fibonacci Example
//
// Computing Fibonacci numbers with tail recursion:
//
// type FibState struct {
// n int
// curr int
// prev int
// }
//
// func fibStep(state FibState) tailrec.Trampoline[FibState, int] {
// if state.n <= 0 {
// return tailrec.Land[FibState](state.curr)
// }
// return tailrec.Bounce[int](FibState{
// n: state.n - 1,
// curr: state.prev + state.curr,
// prev: state.curr,
// })
// }
//
// func fibonacci(n int) int {
// current := tailrec.Bounce[int](FibState{n, 1, 0})
// for {
// bounce, land, isLand := tailrec.Unwrap(current)
// if isLand {
// return land
// }
// current = fibStep(bounce)
// }
// }
//
// # List Processing Example
//
// Summing a list with tail recursion:
//
// type SumState struct {
// list []int
// sum int
// }
//
// func sumStep(state SumState) tailrec.Trampoline[SumState, int] {
// if len(state.list) == 0 {
// return tailrec.Land[SumState](state.sum)
// }
// return tailrec.Bounce[int](SumState{
// list: state.list[1:],
// sum: state.sum + state.list[0],
// })
// }
//
// func sumList(list []int) int {
// current := tailrec.Bounce[int](SumState{list, 0})
// for {
// bounce, land, isLand := tailrec.Unwrap(current)
// if isLand {
// return land
// }
// current = sumStep(bounce)
// }
// }
//
// # Integration with Reader Monads
//
// The tailrec package is commonly used with Reader monads (readerio, context/readerio)
// to implement stack-safe recursive computations that also depend on an environment:
//
// import (
// "github.com/IBM/fp-go/v2/readerio"
// "github.com/IBM/fp-go/v2/tailrec"
// )
//
// type Env struct {
// Multiplier int
// }
//
// func compute(n int) readerio.ReaderIO[Env, int] {
// return readerio.TailRec(
// n,
// func(n int) readerio.ReaderIO[Env, tailrec.Trampoline[int, int]] {
// return func(env Env) func() tailrec.Trampoline[int, int] {
// return func() tailrec.Trampoline[int, int] {
// if n <= 0 {
// return tailrec.Land[int](n * env.Multiplier)
// }
// return tailrec.Bounce[int](n - 1)
// }
// }
// },
// )
// }
//
// # Benefits
//
// - **Stack Safety**: Prevents stack overflow for deep recursion
// - **Clarity**: Maintains the structure of recursive algorithms
// - **Performance**: Converts recursion to iteration without manual rewriting
// - **Composability**: Works well with functional programming patterns
//
// # When to Use
//
// Use trampolines when:
// - You have a naturally recursive algorithm
// - The recursion depth could be large (thousands of calls)
// - You want to maintain the clarity of recursive code
// - You're working with functional programming patterns
//
// # Performance Considerations
//
// While trampolines prevent stack overflow, they do have some overhead:
// - Each step allocates a Trampoline struct
// - The executor loop adds some indirection
//
// For shallow recursion (< 1000 calls), direct recursion may be faster.
// For deep recursion, trampolines are essential to avoid stack overflow.
//
// # Key Functions
//
// **Bounce**: Create a trampoline that continues computation with a new state
//
// **Land**: Create a trampoline that terminates with a final result
//
// **Unwrap**: Extract the state and determine if computation should continue
//
// # See Also
//
// - readerio.TailRec: Tail-recursive Reader IO computations
// - context/readerio.TailRec: Tail-recursive Reader IO with context
package tailrec