"Inside" Functors — Evaluating things more than once

May 1, 2011
By

(This article was first published on Struggling Through Problems, and kindly contributed to R-bloggers)

(The examples here work with the version of insidefunctor tagged as
“v1″)

I ran into an interesting problem using “inside”
functors.

Something is wrong in the following code (well, depending on what you
thought it should do).


> library(insidefunctor)

> `%+.%` = fmap(`+`)

> x = seq(0, 10, len = 50)

> plot(x, collect(each(x) %+.% runif(1)))


It’s clear that in constructions like each(x) + y, y is only
going to be evaluated once. Of course, the preceding example could have been
written


> plot(x, collect(each(x) %+.% each(runif(length(x)))))


but I think that that is not as grammatically pretty.

But, since we solved the last grammatical problem with a hacky use of
inside-functors, why not try the same trick? Say we define an inside functor
meval (for multiple-evaluations) that behaves like this:

  • meval(expr) returns a promise to evaluate expr
  • func(meval(expr)) returns a promise to evaluate func(expr)
  • collect(meval(expr)) evaluates it finally.

That is, the unevaluated chain keeps growing until it is finally collected,
at which point a value results.

So let’s define that.


> meval = function(expr, level=1) {

> expr = substitute(expr)

> callback = function () {

> eval(expr)

> }

> make.meval(callback, level=level, depth=1)

> }

> make.meval = function(callback, level, depth) {

> functor = inside.functor(level, depth)

> functor$callback = callback

>

> class(functor) = c(‘meval’, class(functor))

>

> functor

> }

> apply.functor.meval = function(

> inside,

> func,

> args,

> caller

> )

> {

> our.level = level(inside)

>

> args.boxed = args

> for (i in seq_along(args.boxed)) {

> arg = args.boxed[[i]]

>

> if (is.inside.functor(arg) && level(arg)>=our.level) {

> }

> else {

> args.boxed[[i]] = list(

> callback = function() {

> arg

> }

> )

> }

> }

> max.depth = max(sapply(args.boxed, depth))

>

> callback = function() {

> piece.args = lapply(args.boxed, function (arg) {

> arg$callback()

> })

> caller(func, piece.args)

> }

>

> make.meval(

> callback,

> level = our.level,

> depth = max.depth

> )

> }

> collect.end.meval = function(inside) {

> inside$callback()

> }


And test it.


> promise = meval(runif(1))

> collect(promise)


0.633877807762474

> collect(promise)


0.236430999357253

Works so far. Now try the motivating example:


> plot(x, collect.all(each(x, l = 2) %+.% meval(runif(1))))


Oh god no it’s this
problem
again.

arg isn’t being remembered in


> args.boxed[[i]] = list(

> callback = function() {

> arg

> }

> )


so the fix is to


> apply.functor.meval = function(

> inside,

> func,

> args,

> caller

> )

> {

> our.level = level(inside)

>

> args.boxed = args

> for (i in seq_along(args.boxed)) {

> arg = args.boxed[[i]]

>

> if (is.inside.functor(arg) && level(arg)>=our.level) {

> }

> else {

> args.boxed[[i]] = (function(arg) {

> force(arg)

> list(

> callback = function() {

> arg

> }

> )

> })(arg)

> }

> }

> max.depth = max(sapply(args.boxed, depth))

>

> callback = function() {

> piece.args = lapply(args.boxed, function (arg) {

> arg$callback()

> })

> caller(func, piece.args)

> }

>

> make.meval(

> callback,

> level = our.level,

> depth = max.depth

> )

> }


Which is ugly but works. Then:


> plot(x, collect.all(each(x, l = 2) %+.% meval(runif(1))))


Now the real challenge is to understand why the above code works,
but interchanging the levels (ie making the each() happen before
the meval()) does not:


> plot(x, collect.all(each(x) %+.% meval(runif(1), l = 2)))


And, given that you obviously wanted it to go the first way or why
would you have used meval(), is there any way to modify the semantics
so that only the first way makes sense (and is that a good idea?),
which brings us to…

About those levels…

They’re yucky. Also note that the call to collect.all
in the preceding example is really doing 2 collects, even though
the functors are only ever written 1 deep.

The reason is that expressions like


> x = c(1, 2, 3)

> y = c(4, 5)

> collect.all(each(x, l = 2) %+.% each(y))



  • 5  6

  • 6  7

  • 7  8


behave like (inserting for the xs)


> collect.all(

> each(x)

> %+.%

> each(

> lapply(x, function(x.) each(y))

> )

> )



  • 5  6

  • 6  7

  • 7  8


which behaves like (inserting for the ys)


> collect.all(

> each(

> lapply(x, function(x.)

> each(

> lapply(y, function(y) x.)

> )

> )

> )

> %+.%

> each(

> lapply(x, function(x) each(y))

> )

> )



  • 5  6

  • 6  7

  • 7  8


ie only when the levels are the same do the eaches “line up” and
remain a single each. When the levels are different they “miss” each
other and become two nested eaches. This is by design but it still
feels messy.

Suppose we were to bring back the suggestion of the name “corresponding”
that we mentioned earlier:


> each(x) %+.% corresponding(y)


would stand for when the levels are identical; in any other case
the levels would be assumed to be different and the functors would
“overlap”.

The advantage to this notation is that only when the word “each” is
actually used is another level introduced. Plus it aligns more closely
with English.

To leave a comment for the author, please follow the link and comment on his blog: Struggling Through Problems.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.