R: A framework for working in a functional programming paradigm...
futile.paradigm-package
R Documentation
A framework for working in a functional programming paradigm in R
Description
Provides dispatching implementations suitable for functional
programming paradigms. The framework provides a mechanism for attaching guards
to functions similar to Erlang, while also providing the safety of assertions
reminiscent of Eiffel.
Details
Package:
futile.paradigm
Type:
Package
Version:
2.0.4
Date:
2012-02-06
License:
LGPL-3
LazyLoad:
yes
In many ways R is a functional language. The futile.paradigm complements R's
functional strengths to make it easier to develop software in this paradigm.
This package implements a function dispatching method based on the guard
concept (in lieu of direct pattern matching) for functional programming in R.
In contrast to UseMethod, which is used for S3 object-oriented
method dispatching, futile.paradigm introduces UseFunction for functional
programming. Since UseMethod only detects the type of the first argument,
dispatching can be tricky/cumbersome when multiple arguments and/or types are
supported. Also, the direct manipulation of class attributes is dangerous in
the S3 paradigm. In the futile.paradigm, access is restricted via encapsulation
to reduce the fragility of such a mechanism. In lieu of the S4 style that
requires significant up-front design, the futile.paradigm intends to ease
the development process from initial experimentation to systems development.
This package provides a richer syntax for defining functions
including the constraints that must be satisfied in order to dispatch to the
particular function. A happy consequence of this approach is that code becomes
self-documenting and functions are more concise since there is a clean
separation of concerns between the true logic of a function and the data
management tasks within the function.
Concrete implementations of the abstract function, must be defined in a
hierarchical naming scheme based on the name of the abstract function.
Similar to UseMethod,
the concrete function name is derived from the abstract function with a unique
suffix separated by a dot. Unlike UseMethod, the suffix is arbitrary as the
dispatching and association is controlled by the guard and the number of
arguments in the concrete function. Guards are expressions using the same
variables as the concrete function variant or explicit functions operating
on the same number of arguments as the concrete function it is guarding.
Either form of a guard must return a boolean value.
Multiple guard functions can be defined in the guard call and in this situation
all guards must resolve to TRUE for the function to execute. In addition to a
function, a default function can be defined by setting the guard expression
to TRUE.
To use the package, two key operators are introduced to implement guard
statements and assertions. Guards are declared by using the '%when%'
operator, which is reminiscent of the Erlang approach to guards.
By convention the guard is declared before the function definition as this
communicates immediately the criteria required to execute this particular
implementation of the parent function. See the examples section for a trivial
implementation.
In addition to guard statements, this package provides post-assertions
reminiscent of Eiffel's design-by-contract approach. Using the '%must%'
operator validates function outputs and fails if they aren't satisfied.
The arguments to a generated assertion are ordered with the result object
first (named 'result'), then all of the function arguments.
Conventions:
. Types are named in PascalCase
. Functions are named in lower case with words separated by underscore
. Function namespaces are separated by dot
. Declare guard statements before concrete implementations
. Declare ensure statements after concrete implementations
. Avoid explicit UseFunction definitions
. Avoid default function implementations unless a package defines all possible
variants
Note
Due to scoping rules with operators, logical expressions need to be
wrapped in parentheses.
Author(s)
Brian Lee Yung Rowe
Maintainer: Brian Lee Yung Rowe <r@nurometic.com>
References
Some background on using guards and pattern matching:
http://en.wikipedia.org/wiki/Guard_%28computing%29
See Also
UseFunction, %when%, %must%
Examples
# The guard must be defined before the concrete function variant
abs_max %when% is.numeric(a)
abs_max %also% (length(a) > 1)
# This adds a post-assertion to ensure the value is what you expect
abs_max %must% (result > 0)
abs_max %as% function(a) abs_max(a[1], abs_max(a[2:length(a)]))
abs_max %when% (is.numeric(a) && length(a) == 1)
abs_max %must% (result == a)
abs_max %as% function(a) a
abs_max %when% (a %isa% DataObject & a %hasa% data)
abs_max %as% function(a) abs_max(as.numeric(strsplit(a$data, ',')[[1]]))
abs_max %when% (is.numeric(a) & is.numeric(b))
abs_max %must% (result >= a | result >= b)
abs_max %as% function(a, b) max(abs(a), abs(b))
# Using a guard of TRUE acts as a default condition
abs_max %when% TRUE
abs_max %as% function(a,b) abs_max(as.numeric(a), as.numeric(b))
# Define constructor for DataObject
create.DataObject <- function(T, data, name=NA) list(name=name, data=data)
# Test some output
abs_max(2,-3) # Calls abs_max.twoArg
abs_max("3","-4") # Calls abs_max.twoArgDefault
abs_max(3,"-4") # Calls abs_max.twoArgDefault
a <- rnorm(10)
abs_max(a) # Calls abs_max.recursive1
b <- create(DataObject, c('12,-3,-5,8,-13,3,1,3'))
abs_max(b) # Calls abs_max.csv
## Newton-Raphson optimization
converged <- function(x1, x0, tolerance=1e-6) abs(x1 - x0) < tolerance
minimize <- function(x0, algo, max.steps=100)
{
step <- 0
old.x <- x0
while (step < max.steps)
{
new.x <- iterate(old.x, algo)
if (converged(new.x, old.x)) break
old.x <- new.x
}
new.x
}
iterate %when% (algo %isa% NewtonRaphson)
iterate %as% function(x, algo) x - algo$f1(x) / algo$f2(x)
iterate %when% (algo %isa% GradientDescent)
iterate %as% function(x, algo) x - algo$step * algo$f1(x)
create.GradientDescent <- function(T, f1, step=0.01) list(f1=f1,step=step)
fx <- function(x) x^2 - 4
f1 <- function(x) 2*x
f2 <- function(x) 2
algo <- create(NewtonRaphson, f1=f1,f2=f2)
minimize(3, algo)
algo <- create(GradientDescent, f1=f1,step=0.1)
minimize(3, algo)