This is a quick post to announce lambda.r version 1.1.0 is released and available on CRAN.^{1} This release has a handful of important new features and bug fixes:

- [new] Type variables in type constraints
- [new] Auto-replacement of function clauses
- [bug] Function types break in type constraints
- [bug] Zero argument functions don’t dispatch properly

### Type Variables

I’ll discuss type variables in full in a separate post, but the basic idea is that you can retain polymorphism of functions by using type variables instead of concrete types. In other words, type variables define the relationship between arguments but not the actual type. Take for instance the Heaviside step function. This function will evaluate equally well for a float, integer, or double. (In R, these are all represented by numeric, so this is somewhat contrived). The output of the function is 0, 0.5 (if x == 0), or 1. Essentially the return type should match the input type.

heaviside(n) %::% a : a heaviside(n) %when% { n < 0 } %as% 0 heaviside(0) %as% 0.5 heaviside(n) %as% 1

Suppose instead the output of the function is 0 or 1. We can represent the return value as a logical. We still don’t care about the input type, so we can define the type constraint as

heaviside(n) %::% a : logical heaviside(n) %when% { n <= 0 } %as% FALSE heaviside(n) %as% TRUE

In short, type variables are another tool to manage how functions are dispatched. Used in conjunction with concrete types, you can achieve generality while preserving granularity.

### Auto-Replacement of Function Clauses

Before this release, overwriting a specific function clause required either sealing the definition or deleting it from the environment. If you skipped this step then new function clauses would continue to be appended to the function. Not only was this annoying it also prevented you from interactively modifying function clauses. Lambda.r is now smart enough to recognize existing function clauses and replace the specific clause.

> fib(0) %as% 5 > fib(1) %as% 2 > fib(n) %as% { fib(n-1) + fib(n-2) } > > fib(0) %as% 1 > fib(1) %as% 1 > > fib(5) [1] 8

The one exception is when there are two function signatures only differentiated by type. In this situation, lambda.r has no way of knowing which clause to replace. The solution is that there is always one type constraint in scope. Hence any ties will be resolved by the type constraint that is in scope. To set the type constraint that is in scope for a function, simply redeclare the type constraint. Let’s define a simple function generator that multiplies an input by some number.

times.n(n) %::% numeric : Function times.n(n=1) %as% { function(x) x + n } times.n(n) %::% character : Function times.n(n) %as% { times.n(as.numeric(n)) }

We source this and try it out for the default case.

> f <- times.n() > f(4) [1] 4

All good, so let’s check it out with a different multiplier.

> f <- times.n(2) > f(4) [1] 6

Whoops, it looks like we have a bug. We need to update the first function clause to use * instead of +. Since the two clauses have the same function signature we need to tell lambda.r which type constraint is in scope.

> times.n(n) %::% numeric:Function > times.n(n) %as% { function(x) x * n } > f <- times.n(2) > f(4) [1] 8

Hence functions can be interactively modified as well as re-sourced with the same behavior. It does imply that if you use type constraints in a function definition, then you need to use them consistently in that function definition.

In some ways auto-replace should merely produce a yawn as a reaction. This is because the behavior is what you expect anyway. While the implementation is non-trivial, my hope is that it is an obvious, almost trivial feature.

^{1} There are actually two versions 1.1.0-2, which supports the 2.15.x R series and 1.1.0-3, which is compatible with the 3.0.x R series. Selection of versions should be automatic. Once R 3 is released in the spring, I plan on supporting the 2.x series for the remainder of the year.

Abe

said:Man, the only flaw of this library I can see right away is that it’s not yet part of default R🙂

Well, included it into my template for R files – right after AGPLv3 clause.

You’ve done awesome job – it’s the first time I’ve stumbled upon an R package which is so universally useful that it’s worth using in ANY script I would ever write in R.

LikeLike

Brian Lee Yung Rowe

said:Thanks for the testimonial! Please spread the word — if enough interest grows around using functional programming within R, perhaps it could be included in the core.

LikeLike