After a four month simmer on various back burners and package conflicts, I’m pleased to announce that the successor to futile.paradigm is officially available on CRAN. The new package is lambda.r (source on github), which hopefully conveys the purpose of the library better than its whimsical predecessor. In some ways this new version deserves a more serious name as the package has matured quite a bit not to mention is part and parcel of a book I’m writing on computational systems and functional programming.

So what exactly is lambda.r? Put simply, lambda.r gives you functional programming in the R language. While R has many functional features built into the language, application development in R is a decidedly object-oriented affair. I won’t go into all the reasons why it’s better to write computational systems in a functional paradigm since that is covered in depth in my forthcoming book “Computational Finance and the Lambda Calculus”. However, here are the salient points:

- Conceptual consistency with mathematics resulting in less translation error between model and system (see my slides from R/Finance 2011)
- Modular and encapsulated architecture that makes growth of a system easier to manage (not to mention easier to accommodate disparate computing needs — think parallel alternatives of the same processing pipeline)
- Efficiency in application development since wiring is trivial

## Features

The fundamental goal of lambda.r is to provide a solid architectural foundation that remains intact through the prototyping and development phases of a model or application. One half is accomplished with a functional syntax that builds in modularity and encapsulation into every function. The other half is through the incremental adoption of constraints in the system. This article will focus primarily on the features, and in a separate article I will outline how to best leverage these features as a system matures.

### Pattern Matching

Functional languages often use pattern matching to define functions in multiple parts. This syntax is reminiscent of sequences or functions with initial values in addition to multi-part definitions. Removing control flow from function definitions makes functions easier to understand and reduces the translation error from math to code.

#### Fibonacci Sequence

For example, the ubiquitous Fibonacci sequence is defined as

, where

In standard R, one way to define this is with an if/else control block or function [1].

fib <- function(n) { ifelse(n < 2, 1, fib(n - 1) + fib(n - 2)) }

Using lambda.r, pattern matching defines the function in three clauses. The behavior of the function is free of clutter as each clause is self-contained and self-explanatory.

fib(0) %as% 1 fib(1) %as% 1 fib(n) %as% { fib(n-1) + fib(n-2) }

#### Heaviside Step Function

When represented as a piecewise constant function, the Heaviside step function is defined in three parts. [2]

Using pattern matching in lambda-r, the function can be defined almost verbatim.

h.step(n) %when% { n < 0 } %as% 0 h.step(0) %as% 0.5 h.step(n) %as% 1

In languages that don’t support pattern matching, again if/else control structures are used to implement these sorts of functions, which can get complicated as more cases are added to a function. A good example of this is the ‘optim’ function in R, which embeds a number of cases within the function definition.

### Guard Statements

The last example sneaks in a guard statement along with pattern matching. Guards provide a rich vocabulary to control when a specific function clause is executed. Each guard statement is a logical expression. Multiple expressions can be present in a guard block, so that the function clause only executes when all the expressions evaluate to TRUE. Using the Fibonacci example above, we can add an argument check to only allow integers.

fib(0) %as% 1 fib(1) %as% 1 fib(n) %when% { is.integer(n) n > 1 } %as% { fib(n-1) + fib(n-2) }

If none of the clauses are satisfied, lambda.r will complain telling you that it couldn’t find a matching function clause.

> fib(2) Error in UseFunction("fib", ...) : No valid function for 'fib(2)' > fib(as.integer(2)) [1] 2

Note: If you are running the examples as you are reading along, then you need to either seal() the functions or rm() the current definition prior to redefining the function. The reason is that function clauses are additive. You can add as many clauses as you want, and they will be evaluated in the order they were declared. Since lambda.r has no way of knowing when you are done defining your function you must explicitly tell it via the seal() function.

### Types

Custom types can be defined in lambda.r. These types can be used in type constraints to provide type safety and distinguish one function clause from another. All types must be defined using PascalCase.

#### Type Constructors

A type constructor is simply a function that creates a type. The name of the function is the name of the type. The return value will automatically be typed while also preserving existing type information. This means that you can create type hierarchies as needed.

Point(x,y) %as% list(x=x,y=y) Polar(r,theta) %as% list(r=r,theta=theta)

In this example we use a list as the underlying data structure. To create an instance of this type simply call the constructor.

point.1 <- Point(2,3) point.2 <- Point(5,7)

Under the hood lambda.r leverages the S3 class mechanism, which means that lambda.r types are compatible with S3 classes.

#### Type Constraints

Types by themselves aren’t all that interesting. Once we define the types, they can be used as constraints on a function.

distance(a,b) %::% Point : Point : numeric distance(a,b) %as% { ((a$x - b$x)^2 + (a$y - b$y)^2)^.5 } distance(a,b) %::% Polar : Polar : numeric distance(a,b) %as% { (a$r^2 + b$r^2 - 2 * a$r * b$r * cos(a$theta - b$theta))^.5 }

As shown above each function clause can have its own constraint. Since type constraints are greedy, a declared constraint will apply to every successive function clause until a new type constraint is encountered.

> distance(point.1, point.2) [1] 5

### Attributes

Types are great for adding structure and safety to an application. However types can have diminishing returns as more types are introduced. In general lambda.r advocates using existing data structures where possible to minimize type clutter. Of course if data.frames and matrices are used for most operations, how do you differentiate function clauses? The answer of course are attributes, which come standard with R. Attributes can be considered meta-data that is orthogonal to the core data structure. They are preserved during operations, so can be carried through a process. Lambda.r makes working with attributes so easy that they should become second nature fairly quickly.

With lambda.r you can access attributes via the ‘@’ symbol. Define them in a type constructor as shown below.

Temperature(x, system='metric', units='celsius') %as% { x@system <- system x@units <- units x }

Function clauses can now be defined based on the value of an attribute.

freezing(x) %::% Temperature : logical freezing(x) %when% { x@system == 'metric' x@units == 'celsius' } %as% { if (x < 0) { TRUE } else { FALSE } } freezing(x) %when% { x@system == 'metric' x@units == 'kelvin' } %as% { if (x < 273) { TRUE } else { FALSE } }

It is trivial then to check whether a given temperature is freezing, based on the units. This approach can be extended to objects like covariance matrices to preserve information that is normally lost in the creation of the matrix (e.g. number of observations).

ctemp <- Temperature(20) freezing(ctemp)

Note that the Temperature type extends the type of ‘x’, so it is also a numeric value. This means that you can add a scalar to a Temperature object, and everything behaves as you would expect.

> ctemp1 <- ctemp - 21 > freezing(ctemp1) [1] TRUE

The combination of types and attributes are two essential tools in the lambda.r toolkit. In this section I’ve also illustrated how S3 classes can naturally be mixed and matched with lambda.r classes.

### Introspection

The goal of lambda.r is to provide rich functionality with a simple and intuitive syntax. To accomplish this there is a lot of wiring behind the scenes. While most of the implementation can safely be ignored, there are times when it is necessary to look under the hood for troubleshooting purposes. Lambda.r provides a number of tools to make debugging and introspection as simple as possible.

The default output of a lambda.r function or type gives a summary view of the function clauses associated with this function. This is an abridged view to prevent long code listings from obscuring the high level summary. Any type constraints and guard statements are included in this display as well as default values.

> freezing <function> [[1]] freezing(x) %::% Temperature:logical freezing(x) %when% { x@system == "metric" x@units == "celsius" } %as% ... [[2]] freezing(x) %::% Temperature:logical freezing(x) %when% { x@system == "metric" x@units == "kelvin" } %as% ...

Index values prefix each function clause. Use this key when looking up the definition of an explicit function clause with the ‘describe’ function.

> describe(freezing,2) function(x) { if ( x < 273 ) { TRUE } else { FALSE } } <environment: 0x7f8cfcd7de60>

#### Debugging

Since lambda.r implements its own dispatching function (UseFunction), you cannot use the standard ‘debug’ function to debug a function clause. Instead use the supplied ‘debug.lr’ and ‘undebug.lr’. These functions will allow you to step through any of the function clauses within a lambda.r function.

## Examples

All examples are in the source package as unit tests. Below are some highlights to give you an idea of how to use the package.

### Prices and Returns

This example shows how to use attributes to limit the scope of a function for specific types of data. Note that the definition of Prices makes no restriction on series, so this definition is compatible with a vector or data.frame as the underlying data structure.

Prices(series, asset.class='equity', periodicity='daily') %as% { series@asset.class <- asset.class series@periodicity <- periodicity series } returns(x) %when% { x@asset.class == "equity" x@periodicity == "daily" } %as% { x[2:length(x)] / x[1:(length(x) - 1)] - 1 }

### Taylor Approximation

This is a simple numerical implementation of a Taylor approximation.

fac(1) %as% 1 fac(n) %when% { n > 0 } %as% { n * fac(n - 1) } d(f, 1, h=10^-9) %as% function(x) { (f(x + h) - f(x - h)) / (2*h) } d(f, 2, h=10^-9) %as% function(x) { (f(x + h) - 2*f(x) + f(x - h)) / h^2 } taylor(f, a, step=2) %as% taylor(f, a, step, 1, function(x) f(a)) taylor(f, a, 0, k, g) %as% g taylor(f, a, step, k, g) %as% { df <- d(f,k) g1 <- function(x) { g(x) + df(a) * (x - a)^k / fac(k) } taylor(f, a, step-1, k+1, g1) }

Use the following definitions like so:

> f <- taylor(sin, pi) > v <- f(3.1)

MIke Spaner (@mspan)

said:Great work! I’ve been fascinated by the goings on of F# and Scala – thanks for bringing it to R.

LikeLike

Brian Lee Yung Rowe

said:Thanks for the kind words. Please let me know if you end up using it and what other features you are interested in. I have considered adding a check for side effects in function definitions but haven’t decided whether people would find that useful or not.

LikeLike

Sonny

said:Man, that’s just brilliant!

I’ve started to learn R this year through some of coursera.org classes – and it was exactly this kind of thing I was missing coming from Haskell.

You, sir, are fucken awesome!

LikeLike

Brian Lee Yung Rowe

said:Thanks for the shout out. I’m a big believer in functional programming for analytical systems. The type declaration syntax is pulled from Haskell, while the guard syntax is inspired by Erlang. My hope is that the overall syntax and approach is consistent with R’s dynamic nature and idioms.

LikeLike

Pingback: Confident package releases in R with crant « Cartesian Faith

Pingback: R for Quants, Part I (B) « Cartesian Faith

rgrannell1

said:I’m really looking forward to using this package! The type-system seems neater than S3 classes for a lot of the way I write code.

Is there a convenient way to define custom print methods for types? For example something analogous to print.point(p) -> cat(‘(‘, p$x, ‘,’, p$y, ‘/n’). Or is this achieved through the standard class systems manually?

LikeLike

Brian Lee Yung Rowe

said:Hey there. For custom print methods you can use a standard S3 generic as lambda.r is backwards compatible with S3.

print.Point <- function(p) sprintf("(%s,%s)\n", p$x, p$y)

LikeLike

Pingback: Infinite generators in R « Cartesian Faith

K..O

said:Hello,

I am new to R and am interested in learning about its functional programming capabilities. Your project looks to be interesting and pertinent to this. One thing is that Im not sure how to install lamda.r into R. I saw on github something about “Install from github using devtools: install_github(‘lambda.r’,’zatonovo’)”but Im not too familiar with github either. Could you please assist me in setting up lamda.r?

Thanks

Kenan

LikeLike

Brian Lee Yung Rowe

said:From CRAN, just run install.packages(‘lambda.r’) and follow the instructions regarding choice of mirror. If you use devtools, then you need to install that first, i.e. install.packages(‘devtools’).

Then from R,

You don’t need an account on github for this. Good luck!

LikeLike

K..O

said:I got the package to install but when I try the example for fib its not working:

this is the script:

fib(0) %as% 1

fib(1) %as% 1

fib(n) %as% { fib(n-1) + fib(n-2) }

I get this error:

Error: could not find function “%as%”

Execution halted

i also tried using the .libPaths function to get the package to work. that didnt work either.

pls help if you can.

LikeLike

Brian Lee Yung Rowe

said:You might want to brush up on your R a little. Try this first:

library(lambda.r)

Then the example will work.

LikeLike

K..O

said:Thanks. Im very green. youre right. : ]

LikeLike

K..O

said:Hi. Are you familiar with Haskell? A lot of the features you implement with lambda.r are similar to features from it. There is an example of how to create a binary search tree using haskell here: http://learnyouahaskell.com/making-our-own-types-and-typeclasses (its halfway down the page) Do you think it would be possible to implement a BST in a similar manner using R?

Thanks again.

LikeLike

Brian Lee Yung Rowe

said:I’m familiar with Haskell but not a big user of it. I did borrow some concepts from Haskell, although more come from Erlang. It’s probably possible to create a BST using a list in R and lambda.r syntax. I don’t know how fast it would be though.

LikeLike

K..O

said:thanks

LikeLike

myschizobuddyBuddy

said:even after using the seal function like seal(fib). it still errors out Error in UseFunction(fib, “fib”, …) : No valid function for ‘fib(4)’

LikeLike

myschizobuddy

said:this is confusing. for fib(1) works but fib(2) won’t and you need fib(as.integer(2)). this is lot more confusing than an if-else statement.

LikeLike

Brian Lee Yung Rowe

said:What version of R and lambda.r are you using? Here is mine:

> sessionInfo()

R version 3.0.2 (2013-09-25)

Platform: x86_64-apple-darwin12.5.0 (64-bit)

…

other attached packages:

[1] lambda.r_1.1.6

And the example:

> fib(0) %as% 1

> fib(1) %as% 1

> fib(n) %as% { fib(n-1) + fib(n-2) }

> fib(4)

[1] 5

LikeLike

myschizobuddy

said:R help for is.integer says NOT to use is.integer to test if x is an integer. Instead it gives another function is.wholenumber to test if the number is indeed an integer.

LikeLike

Brian Lee Yung Rowe

said:The example you are referring to is meant to be illustrative. Keep in mind that it is purely for pedagogical purposes.

LikeLike

myschizobuddy

said:Can you please also post the math of taylor approximation so i can clearly see the math to code translation.

LikeLike

Mert Nuhoglu

said:Thank you for this great library.

It works in my computer but recursive function definitions take a lot of time to complete. For example, fib(100) takes more than 15 mins.

LikeLike

Brian Lee Yung Rowe

said:Thanks for the kind words. Yes, recursion is slow. At some point I’d like to implement tail recursion in R, but it’s non-trivial.

LikeLike

Brian Lee Yung Rowe

said:So I wrote a naive version of fib, and it takes equally long (actually longer), so it’s less an issue with lambda.r and more about recursion in R.

LikeLike