NAME

Sidef::Types::Block::Block

DESCRIPTION

This class implements blocks (closures/anonymous functions) in Sidef. Blocks are fundamental constructs that encapsulate executable code and can capture variables from their surrounding scope. They are first-class objects that can be passed as arguments, returned from functions, and stored in variables.

Blocks in Sidef support:

  • Lexical closures with captured variables

  • Lazy evaluation and memoization

  • Functional programming patterns (map, grep, compose)

  • Parallel execution (threads and forks)

  • Mathematical operations (summation, product, composition)

SYNOPSIS

# Simple block creation
var square = {|n| n * n }
say square(5)           #=> 25

# Block with multiple parameters
var add = {|a, b| a + b }
say add(3, 7)           #=> 10

# Blocks as iterators
var nums = [1, 2, 3, 4, 5]
nums << {|n| say n }    # Print each number

# Functional composition
var double = {|x| x * 2 }
var inc = {|x| x + 1 }
var f = (double ∘ inc)
say f(5)                #=> 12  # (5+1)*2

# Memoization
var fib = {|n|
    n <= 1 ? n : (fib(n-1) + fib(n-2))
}.cache
say fib(40)             # Fast due to caching

INHERITS

Inherits methods from:

* Sidef::Object::Object

METHODS

&

block & condition

Filters elements using the block as a predicate function. Returns elements for which the block returns true.

var nums = [1, 2, 3, 4, 5, 6]
var evens = (nums & {|n| n.is_even })
say evens           #=> [2, 4, 6]

Aliases: grep

*

block * n

Repeats the block execution n times, returning an array of results.

var roll_die = { 6.rand.int + 1 }
var rolls = (roll_die * 10)
say rolls           #=> [3, 1, 6, 2, 5, 4, 1, 6, 3, 2]

Aliases: repeat

<<

array << block

Iterates over array elements, calling the block with each element. Used for side effects.

var numbers = [1, 2, 3, 4, 5]
numbers << {|n| say "Number: #{n}" }
# Outputs:
# Number: 1
# Number: 2
# Number: 3
# Number: 4
# Number: 5

Aliases: for, each, foreach

>>

array >> block

Maps array elements through the block, returning a new array with transformed values.

var numbers = [1, 2, 3, 4, 5]
var squares = (numbers >> {|n| n * n })
say squares         #=> [1, 4, 9, 16, 25]

Aliases: map

Π

block.Π(range)

Calculates the product of all values returned by calling the block for each value in the range.

var factorial = {|n| n }
say factorial.Π(1..5)      #=> 120  # 1*2*3*4*5

var prod = {|n| n**2 }
say prod.Π(1..4)            #=> 576  # 1*4*9*16

Aliases: prod

Σ

block.Σ(range)

Calculates the sum of all values returned by calling the block for each value in the range.

var identity = {|n| n }
say identity.Σ(1..10)       #=> 55  # Sum of 1 to 10

var squares = {|n| n**2 }
say squares.Σ(1..5)         #=> 55  # 1+4+9+16+25

Aliases: sum

block_a ∘ block_b

Returns a new block that represents the composition of two blocks. The result is equivalent to block_a(block_b(x)).

var double = {|x| x * 2 }
var square = {|x| x ** 2 }
var f = (double ∘ square)
say f(5)            #=> 50  # double(square(5)) = double(25) = 50

# Multiple composition
var inc = {|x| x + 1 }
var g = (square ∘ double ∘ inc)
say g(3)            #=> 64  # square(double(inc(3))) = square(double(4)) = square(8) = 64

Aliases: compose

array_identity

block.array_identity

Returns an identity block that returns an empty array. Used internally for certain operations.

var block = { ... }
var id = block.array_identity
say id()            #=> []

bsearch

block.bsearch(range)

Performs a binary search on the range using the block as a comparison function. The block should return true for values greater than or equal to the target.

var find_sqrt = {|x| x**2 >= 50 }
say find_sqrt.bsearch(0..100)   #=> 8  # First integer where x^2 >= 50

bsearch_ge

block.bsearch_ge(range)

Binary search that finds the first element in range where the block returns a value greater than or equal to zero.

var block = {|x| x**2 - 30 }
say block.bsearch_ge(0..100)    #=> 6  # First x where x^2 >= 30

bsearch_inverse

block.bsearch_inverse(value, range)

Performs an inverse binary search to find the input that produces the given value when passed to the block.

var square = {|x| x**2 }
say square.bsearch_inverse(49, 0..100)  #=> 7

bsearch_le

block.bsearch_le(range)

Binary search that finds the last element in range where the block returns a value less than or equal to zero.

var block = {|x| 50 - x**2 }
say block.bsearch_le(0..100)    #=> 7  # Last x where 50-x^2 >= 0

cache

block.cache

Returns a memoized version of the block that caches results based on input arguments. Subsequent calls with the same arguments return cached results instead of recomputing.

var expensive = {|n|
    say "Computing fib(#{n})"
    n <= 1 ? n : (expensive(n-1) + expensive(n-2))
}.cache

say expensive(5)    # Computes only once per unique input
say expensive(5)    # Returns cached result

call

block.call(*args)

Calls the block with the provided arguments and returns the result.

var multiply = {|a, b| a * b }
say multiply.call(6, 7)     #=> 42

# Equivalent to:
say multiply(6, 7)          #=> 42

cap

block.cap

Captures the current state of the block and its environment, returning a callable object. This preserves the lexical scope at the time of capture.

var x = 10
var block = { x * 2 }
var captured = block.cap
x = 20
say captured()      #=> 20  # Uses current x value

Aliases: capture

do

block.do

Executes the block without arguments and returns the result. Useful for immediate execution of parameterless blocks.

var result = { 
    var x = 5
    var y = 10
    x + y 
}.do

say result          #=> 15

Aliases: run

exec

block.exec(*args)

Executes the block with the given arguments. Similar to call but may have different semantics in certain contexts.

var greet = {|name| "Hello, #{name}!" }
say greet.exec("Alice")     #=> Hello, Alice!

ffork

block.ffork(*args)

Forks a new process and executes the block in the child process. Returns a future-like object that can be used to retrieve the result. The 'f' stands for "future fork".

var compute = {|n|
    # Heavy computation
    (1..n).sum
}

var future = compute.ffork(1000000)
# Do other work...
say future.wait     # Wait for and get result

Aliases: start

first

block.first(n, range)

Returns the first n values from evaluating the block over the range.

var squares = {|x| x**2 }
say squares.first(5, 1..100)    #=> [1, 4, 9, 16, 25]

flush_cache

block.flush_cache

Clears the memoization cache for a cached block, forcing recomputation on subsequent calls.

var block = {|n| 
    say "Computing..."
    n * 2 
}.cache

block(5)            # Prints "Computing..."
block(5)            # Uses cache
block.flush_cache
block(5)            # Prints "Computing..." again

fork

block.fork(*args)

Forks a new process and executes the block in the child process with the given arguments. Returns the child process PID.

var pid = {
    say "Child process: #{$$ }"
    # Do work in child
}.fork

say "Parent process: #{$$ }, child PID: #{pid}"

identity

block.identity

Returns a block that acts as the identity function, returning its input unchanged. Used internally for functional programming operations.

var id = block.identity
say id(42)          #=> 42
say id([1,2,3])     #=> [1, 2, 3]

if

block.if(condition)

Conditionally executes the block if the condition is true.

var action = { say "Condition was true!" }
action.if(5 > 3)    # Executes and prints message
action.if(5 < 3)    # Does not execute

is_identity

block.is_identity

Returns true if the block is an identity function (returns its input unchanged), false otherwise.

var id = {|x| x }
say id.is_identity          #=> true

var double = {|x| x * 2 }
say double.is_identity      #=> false

list_identity

block.list_identity

Returns an identity block that returns its arguments as a list. Used internally for list-oriented operations.

var lid = block.list_identity
say lid(1, 2, 3)    #=> [1, 2, 3]

loop

block.loop

Executes the block in an infinite loop until a break or return statement is encountered.

var counter = 0
{ 
    say counter++
    counter >= 5 && break
}.loop
# Prints: 0, 1, 2, 3, 4

nest

block.nest(n, initial_value)

Applies the block n times, using the result of each application as input for the next. Starts with initial_value.

var double = {|x| x * 2 }
say double.nest(3, 5)       #=> 40  # 5 -> 10 -> 20 -> 40

var inc = {|x| x + 1 }
say inc.nest(10, 0)         #=> 10

new

Block.new(code)

Creates a new block object from code. Typically blocks are created using brace syntax rather than this constructor.

var block = Block.new({|x| x * 2 })
say block(5)        #=> 10

nth

block.nth(n, range)

Returns the nth value (0-indexed) from evaluating the block over the range.

var squares = {|x| x**2 }
say squares.nth(5, 1..100)      #=> 36  # 6th square (0-indexed)

null_identity

block.null_identity

Returns an identity block that returns null/nil. Used internally for operations that need a null-returning identity.

var nid = block.null_identity
say nid()           #=> nil

thr

block.thr(*args)

Executes the block in a new thread with the given arguments. Returns a thread object that can be joined to retrieve the result.

var compute = {|n|
    (1..n).sum
}

var thread = compute.thr(1000)
# Do other work...
say thread.join     # Wait for thread and get result

Aliases: thread

time

block.time

Measures and returns the execution time of the block in seconds.

var duration = {
    # Some computation
    1000.times { |i| i**2 }
}.time

say "Execution took #{duration} seconds"

to_s

block.to_s

Returns a string representation of the block.

var block = {|x| x * 2 }
say block.to_s      #=> "Block(...)"

Aliases: dump, to_str

uncache

block.uncache

Returns an uncached version of a cached block, removing memoization.

var cached = {|n| n * 2 }.cache
var uncached = cached.uncache
# uncached will recompute each time

while

block.while(condition)

Executes the block repeatedly as long as the condition block returns true.

var i = 0
{
    say i
    i++
}.while({ i < 5 })
# Prints: 0, 1, 2, 3, 4

EXAMPLES

Functional Programming

# Map, filter, and reduce operations
var numbers = (1..10)

var evens = numbers.grep{|n| n.is_even }
var doubled = evens.map{|n| n * 2 }
var sum = doubled.sum

say sum             #=> 60

Closures and Scope

func make_counter {
    var count = 0
    return {
        ++count
    }
}

var counter1 = make_counter()
var counter2 = make_counter()

say counter1()      #=> 1
say counter1()      #=> 2
say counter2()      #=> 1  # Independent counter

Parallel Processing

# Process data in parallel using threads
var data = (1..8)
var threads = data.map{|n|
    { n**3 }.thr
}

var results = threads.map{|t| t.join }
say results         #=> [1, 8, 27, 64, 125, 216, 343, 512]

Memoization for Performance

# Recursive Fibonacci with memoization
var fib = {|n|
    n <= 1 ? n : (fib(n-1) + fib(n-2))
}.cache

say fib(100)        # Computes efficiently

SEE ALSO

AUTHOR

Daniel Șuteu (trizen)

COPYRIGHT AND LICENSE

Copyright (C) 2013-2025

This library is free software; you can redistribute it and/or modify it under the same terms as Sidef itself.