class Campa::Lambda

Represents an anonymous function that will be executed in a given {Context}.

@example

# given the representation of
# ((lambda (something) (print something)) "hello world")
lbd = Lambda.new(
  [Symbol.new("something")],
  [List.new(Symbol.new("print"), Symbol.new("something"))]
)

# sends "hello world" to the $stdout and returns
lbd.call("hello world") #=> nil

@example working with closures:

# given the representation of
# (label meaning 42)
# ((lambda (time) (print "time: " time " meaning of life: " meaning)) 420)
ctx = Context.new(Symbol.new("meaning") => 42)

lbd = Lambda.new(
  [Symbol.new("time")],
  [
    List.new(
      Symbol.new("print"),
      "time: ", Symbol.new("time"),
      ", meaning of life: ", Symbol.new("meaning")
    )
  ],
  ctx
)

# sends "time: 420, meaning of life: 42" to $stdout and returns
lbd.call(420) #=> nil

Attributes

body[R]
closure[R]
evaler[R]
params[R]

Public Class Methods

new(params, body, closure = Context.new) click to toggle source

@param params [Array<Symbol>] {Symbol}s naming the parameters

that a lambda can receive when being invoked

@param body [Array<Object>] expressions composing the body,

they are executed one by one in order
in the given {Context}

@param closure [Context] used as a fallback for the {Context}

given when the {Lambda} is executed
# File lib/campa/lambda.rb, line 47
def initialize(params, body, closure = Context.new)
  @params = params
  @body = Array(body)
  @closure = closure
  @evaler = Evaler.new
end

Public Instance Methods

==(other) click to toggle source

Stablishes equality between {Lambda} objects by comparing {#params} and {#body}

@param other [Lambda] another {Lambda} @return [Boolean] true if {#params} names and {#body} expressions

are <i>#==</i> in both {Lambda}.
# File lib/campa/lambda.rb, line 86
def ==(other)
  return false if !other.is_a?(Campa::Lambda)

  params == other.params && body == other.body
end
call(*args, env:) click to toggle source

Executes the expressions contained in the {#body} one by one using as {Context} the parameter env: on this method.

The env: param will be used here as a fallback to a brand new {Context} created in the moment of the invocation. This isolates the {Context} passed as a parameter of any mutations that would be created by the {Lambda} if there is any binding during the execution.

@param args [Array<Object>] the values that will be bound

to each of the {#params} given to the constructor

@param env [Context] for the execution of a lambda @return [Object] result of evaluating the last expression on {#body}

# File lib/campa/lambda.rb, line 69
def call(*args, env:)
  raise arity_error(args) if params.to_a.length != args.length

  @body.reduce(nil) do |_, expression|
    evaler.call(
      expression,
      invocation_env(env, args)
    )
  end
end

Private Instance Methods

arity_error(args) click to toggle source
# File lib/campa/lambda.rb, line 96
def arity_error(args)
  Error::Arity.new("lambda", params.to_a.length, args.length)
end
invocation_env(env, args) click to toggle source
# File lib/campa/lambda.rb, line 100
def invocation_env(env, args)
  closure.push(env.push(Context.new)).tap do |ivk_env|
    params.each_with_index do |symbol, idx|
      ivk_env[symbol] = args[idx]
    end
  end
end