Class: Heist::Runtime::Function
- Inherits:
-
Object
- Object
- Heist::Runtime::Function
- Defined in:
- lib/heist/runtime/callable/function.rb
Overview
There are several types of callable object in Heist: Function, Syntax,
Macro and Continuation. For convenience of type detection, Function
is treated as a the base class for all these, though they all typically
override Function's core methods. The intention is to treat all
these types as uniformly as possible, though this does introduce some
API problems which still need resolving. In any case, the current state
of things:
============
| Function |
============
|
---------------------------
| |
========== ================
| Syntax | | Continuation |
========== ================
|
=========
| Macro |
=========
Heist is designed to be reasonably modular in that none of Scheme's
special forms or built-in functions are mentioned in the parser; instead
they are all implemented as first-class functions and stored in the
global scope when you initialize a Heist Runtime. The Heist runtime
does implement a fair amount of Scheme-specific functionality but you're
not forced to use any of it: at its core it is designed as a general
processor for s-expressions and could be used as a backend for any
Lisp-style language. The only hard-and-fast rule is that, to evaluate
an expression, you evaluate its first element, and call the resulting
function with the remaining elements.
The Heist callable types -- Function, Syntax, Macro and
Continuation -- differ primarily in terms of what they do with their
arguments when called. All are passed the current Scope and a Cons
list containing the rest of the current expression when they are
called; it is up to the type of function being called what to do with
the expression.
The Function class is used for representing the basic idea of a Lisp
procedure; a reusable block of code that can accept zero, one or more
arguments, return output values and cause side effects on the state of
the running program. Heist functions can be implemented using Ruby
blocks or Scheme lists.
For information on other callable types, see Syntax, Macro and
Continuation.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#body ⇒ Object
readonly
Returns the value of attribute body.
-
#name ⇒ Object
Returns the value of attribute name.
Instance Method Summary collapse
-
#apply(params) ⇒ Object
Returns the result of applying the
Functionto the given collection ofparams. -
#call(scope, cells) ⇒ Object
Calls the
Functionwith a callingScope(in which to evaluate the parameters), and aConslist containing the parameter expressions. -
#initialize(scope, formals = nil, body = nil, &block) ⇒ Function
constructor
A
Functioncan be initialized in one of two ways. -
#lazy? ⇒ Boolean
Returns
trueiff theFunctionis lazy, i.e. -
#primitive? ⇒ Boolean
Returns
trueiff theFunctionis a primitive, that is to say it is implemented in Ruby rather than Scheme. -
#to_s ⇒ Object
(also: #inspect)
Returns a string placeholder for the
Function, containing its name if it has one.
Constructor Details
#initialize(scope, formals = nil, body = nil, &block) ⇒ Function
A Function can be initialized in one of two ways. Both types
require a Scope as the first parameter: this is the lexical
scope in which the Function is defined. The other parameters
should consist either of a Ruby block that accepts the procedure's
arguments and returns its result:
env = Scope.new
env['plus'] = Function.new(env) { |a,b| a + b }
Or, the parameters should be a list of formal argument names for the procedure, and a list containing zero or more Scheme expressions that make up the procedure's body. If the list of formals is an improper list, the tail name will be assigned a list of any parameters remaining after all the preceeding formals have been assigned values. If the list of formals is not a list but a single identifier, that identifier will be assigned a list of all the parameters when the procedure is called.
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/heist/runtime/callable/function.rb', line 74 def initialize(scope, formals = nil, body = nil, &block) @formals, @rest = [], formals if Cons === formals tail = formals.tail.cdr @formals = formals.map { |id| id.to_s } @rest = (Identifier === tail) ? tail : nil end @scope = scope @body = body || block @lazy = scope.runtime.lazy? @eager = !scope.runtime.stackless? end |
Instance Attribute Details
#body ⇒ Object (readonly)
Returns the value of attribute body.
54 55 56 |
# File 'lib/heist/runtime/callable/function.rb', line 54 def body @body end |
#name ⇒ Object
Returns the value of attribute name.
54 55 56 |
# File 'lib/heist/runtime/callable/function.rb', line 54 def name @name end |
Instance Method Details
#apply(params) ⇒ Object
Returns the result of applying the Function to the given collection
of params. If the procedure is primitive, this will return a value
immediately. If it's a Scheme function, this simply binds the params
to the procedure's formal arguments and returns a Body with the
procedure's body expressions and the newly allocated Scope. We use
a trampoline to call Functions iteratively to allow tail
call optimisation. (See Stackless, Stack, Frame and Body for
more information.)
119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/heist/runtime/callable/function.rb', line 119 def apply(params) return @body.call(*params) if primitive? closure = Scope.new(@scope) idx = 0 @formals.each do |name| closure[name] = params[idx] idx += 1 end closure[@rest] = Cons.construct(params[idx..-1]) if @rest Body.new(@body, closure) end |
#call(scope, cells) ⇒ Object
Calls the Function with a calling Scope (in which to evaluate the
parameters), and a Cons list containing the parameter expressions.
In lazy mode, the parameter expressions are wrapped in Binding
objects for later execution. If using the continuation-supporting
Stack interpreter (as indicated by the @eager flag), the
parameters will already have been evaluated and should not be
re-evaluated lest we try to interpret data lists as code. Returns the
result of calling apply with the evaluated parameters.
103 104 105 106 107 108 109 |
# File 'lib/heist/runtime/callable/function.rb', line 103 def call(scope, cells) params = cells.map do |arg| lazy? ? Binding.new(arg, scope) : (@eager ? arg : Heist.evaluate(arg, scope)) end apply(params) end |
#lazy? ⇒ Boolean
Returns true iff the Function is lazy, i.e. it does not
evaluate its arguments unless it needs their values. Only non-
primitive functions can be lazy since we have no way of telling
when Ruby needs to access a value. Besides, primitive functions
will typically use all their parameters.
142 143 144 |
# File 'lib/heist/runtime/callable/function.rb', line 142 def lazy? @lazy and not primitive? end |
#primitive? ⇒ Boolean
Returns true iff the Function is a primitive, that is to say it
is implemented in Ruby rather than Scheme.
133 134 135 |
# File 'lib/heist/runtime/callable/function.rb', line 133 def primitive? Proc === @body end |
#to_s ⇒ Object Also known as: inspect
Returns a string placeholder for the Function, containing its
name if it has one.
148 149 150 |
# File 'lib/heist/runtime/callable/function.rb', line 148 def to_s "#<procedure:#{ @name }>" end |