NAME
Sidef::Object::LazyMethod - Lazy method chaining for deferred execution
DESCRIPTION
The LazyMethod class implements lazy evaluation for method chains in Sidef. It stores a sequence of method calls that are not executed immediately, but only when explicitly invoked. This enables building reusable method call sequences, creating functional pipelines, and deferring computation until needed.
LazyMethod objects are created using the method or methods methods available on all objects (via the Object base class), not by calling LazyMethod directly.
SYNOPSIS
# Create a lazy method using the 'method' global method
var lazy = [1,2,3,4,5].method('map', {|x| x * 2})
lazy = lazy.method('grep', {|x| x > 5})
lazy = lazy.method('sum')
# Execute the chain
say lazy.call() # 30
# Or chain and execute in one expression
var result = [1,2,3,4,5].method('map', {|x| x ** 2})
.method('sum')
.call() # 55
# Using the 'methods' global method to get all available methods
var array_methods = [1,2,3].methods()
say array_methods.keys # Returns all method names as an array
# Each value in the hash is a LazyMethod that can be executed
say array_methods{:sum}.call() # 6
# Lazy methods can be stored and reused
var double_map = nil.method('map', {|x| x * 2})
say [1,2,3].method('map', {|x| x * 2}).call() # [2, 4, 6]
say [4,5,6].method('map', {|x| x * 2}).call() # [8, 10, 12]
INHERITS
Inherits methods from:
* Sidef::Object::Object
HOW LAZYMETHOD OBJECTS ARE CREATED
LazyMethod objects cannot be instantiated directly. They are created through two global methods available on all objects:
obj.method(method_name, *args)
Creates a LazyMethod object representing a deferred method call.
Parameters:
method_name- The name of the method to call (as a string)*args- Zero or more arguments to pass to the method when executed
Returns: A LazyMethod object that can be further chained or executed.
Example:
var lazy = "hello".method('uc')
say lazy.call() # "HELLO"
# Chaining multiple method calls
var chain = [1,2,3,4,5].method('grep', {|x| x.is_even})
.method('map', {|x| x ** 2})
.method('sum')
say chain.call() # 20
obj.methods(*args)
Returns a Hash where keys are method names and values are LazyMethod objects representing those methods pre-configured with the given arguments.
Parameters:
*args- Optional arguments to pass to each method when creating the LazyMethod objects
Returns: A Hash mapping method names to LazyMethod objects.
Example:
var methods = [1,2,3].methods()
# Access specific method
say methods{:sum}.call() # 6
say methods{:reverse}.call() # [3, 2, 1]
# List all available methods
say methods.keys.sort
# Methods can be further chained
var result = methods{:map}.method('sum').call({|x| x * 2}) # 12
LAZYMETHOD INSTANCE METHODS
Once a LazyMethod object is created (via method or methods), the following methods are available:
method
lazy.method(method_name, *args)
Adds another method call to the lazy evaluation chain. Returns a new LazyMethod object with the extended chain, allowing fluent chaining.
Parameters:
method_name- The name of the method to call (as a string)*args- Zero or more arguments to pass to the method when executed
Returns: A new LazyMethod object with the extended call chain.
Example:
var lazy = [1,2,3,4,5].method('map', {|x| x ** 2})
lazy = lazy.method('grep', {|x| x > 10})
lazy = lazy.method('sum')
say lazy.call() # 41 (16 + 25)
call
lazy.call(*additional_args)
Executes the entire lazy method chain and returns the final result. Each method is called on the result of the previous method in sequence.
Any additional arguments passed to call are appended to the arguments of the final method in the chain.
Parameters:
*additional_args- Optional additional arguments for the final method call
Returns: The result of executing the complete method chain.
Example:
var lazy = "hello world".method('split')
.method('map', {|s| s.uc})
say lazy.call(' ') # ["HELLO", "WORLD"]
# The ' ' is passed to the first method (split)
var lazy2 = "hello".method('uc').method('chars')
say lazy2.call() # ["H", "E", "L", "L", "O"]
run
lazy.run(*additional_args)
Alias for call. Executes the lazy method chain with optional additional arguments.
Parameters:
*additional_args- Optional additional arguments for the final method call
Returns: The result of executing the complete method chain.
AUTOLOAD BEHAVIOR
The LazyMethod class implements AUTOLOAD to enable implicit execution. Any method called on a LazyMethod object (except call, run, and method) will:
1. Execute the entire lazy chain 2. Call the requested method on the final result
This allows you to execute and immediately operate on the result without explicitly calling .call():
var lazy = [1,2,3].method('map', {|x| x * 2})
# These are equivalent:
say lazy.call().sum() # Explicit execution, then sum
say lazy.sum() # Implicit execution via AUTOLOAD
# Another example
var lazy2 = "hello".method('uc')
say lazy2.chars() # Implicitly executes .uc() then calls .chars()
say lazy2.len() # 5
USE CASES
Building Reusable Transformations
Create reusable transformation pipelines that can be applied to different data:
var normalize = nil.method('map', { .to_n })
.method('grep', { .is_finite })
.method('sort')
var data1 = ["3", "1", "2", "inf"]
var data2 = ["5.5", "2.2", "nan", "3.3"]
say data1.method('map', { .to_n })
.method('grep', { .is_finite })
.method('sort')
.call() # [1, 2, 3]
Method Exploration
Use the methods method to explore available methods on objects:
var str_methods = "hello".methods()
# See what methods are available
say str_methods.keys.sort.first(10)
# Try out methods
say str_methods{:uc}.call() # "HELLO"
say str_methods{:reverse}.call() # "olleh"
say str_methods{:chars}.call() # ["h", "e", "l", "l", "o"]
Deferred Computation
Delay expensive computations until they're actually needed:
func create_processor(data) {
data.method('grep', {|x| x.is_prime })
.method('map', {|x| expensive_calculation(x) })
.method('sum')
}
var lazy_result = create_processor(1..1000)
# No computation has happened yet
if (user_wants_result) {
say lazy_result.call() # Now it executes
}
Functional Composition
Build complex operations by chaining simple transformations:
var process = 1..20 -> method('grep', {|x| x.is_even })
-> method('map', {|x| x ** 2})
-> method('grep', {|x| x > 50})
-> method('sum')
say process.call() # Sum of even squares > 50
Creating Method Aliases
Store commonly used method chains for reuse:
var square_map = nil.method('map', {|x| x ** 2})
var sum_squares = nil.method('map', {|x| x ** 2}).method('sum')
say [1,2,3].concat(square_map.call()) # [1, 2, 3, 1, 4, 9]
say sum_squares.call([1,2,3]) # 14
say sum_squares.call([4,5,6]) # 77
IMPLEMENTATION NOTES
LazyMethod objects store the complete chain of method calls and arguments
Each call to
methodcreates a new LazyMethod with the extended chainExecution happens sequentially: first method on original object, each subsequent method on previous result
The
callandrunmethods are identical in functionalityAUTOLOAD enables syntactic sugar for immediate execution and method chaining
LazyMethod objects are immutable - each
methodcall returns a new object
SEE ALSO
Sidef::Object::Object - Parent class providing the
methodandmethodsmethodsSidef::Object::Lazy - For lazy evaluation of single expressions
Sidef::Types::Block::Block - For lambda/closure syntax used in examples
Sidef::Types::Array::Array - For array methods like map, grep, sum
AUTHOR
Daniel Șuteu
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Sidef itself.