Jolle's slog
Browse All jolle.se RSS Feed

And things beyond currying to bind them

# Fri, 13 Jun 2008 19:33 – No comments

Let's talk a bit about boost::bind.

Well, maybe first I should say that I've not used boost::bind very much. I've just used it a little. Today I've used it some more, but I'm still on the not used very much status. So all of this might be wrong, or I might be too ignorant to know something important about it. But this is how things appeared to me today.

boost::bind basically takes some kind of functor and then let's you attach values to its arguments. That's probably what everyone sees at first, and then they go 'Oh, it's currying,' and moves on and uses boost::bind as if it were just currying (except, I guess, for how it handles unassigned arguments with _1 etc).

At least until one day, when they'll notice that boost::bind tries to be clever. It's not currying. Currying is a simple process and there's nothing really clever behind the scenes that does something it thinks clever. Don't get me wrong, currying is very clever, but all of the cleverness lies in the basic, obvious, simple principle.

So what's this with boost::bind, you say? boost::bind thinks that an argument that is a boost::bind object should be handled specially. It handles it like function composition. Like the documentation says:

bind(f, bind(g, _1))(x);  // f(g(x))

Well, first I would like to say that few who just read a little and figured that boost::bind is basically just currying would have read or guessed that. Not until being bitten by it. Then I would like to say that maybe in that specific example there's some kind of sense in this behaviour. But in general sense, it's just confusing. If I bind a boost::bind object to a function argument, I would probably want the actual object to be bound as the argument. Not to have it magically called in Happy Fun Boost Dream Land.

'But,' you say, 'in that example binding an object as an argument wouldn't make sense. As we clearly call it in the end with just one argument, nothing else would make sense.' That's because that example was produced to match the behaviour. Duh!

In an other little corner of the boost libraries, there's boost::lambda. boost::lambda realizes that there are problems with this behaviour. It gives examples, too. It has a fix for the examples. Unfortunately, both the examples and solution fails to consider all cases and ends up not solving all of them. Want their example and fix loosely quoted?

template <class F> int nested(const F& f)
{
  int x;
  bind(f, _1)(x);
}

int foo(int);
int bar(int, int);

nested(&foo); // works as intended, yay!
nested(bind(bar, 1, _1)); // fail

With the boost::lambda's unlambda, it is solved with:

template <class F> int nested(const F& f)
{
  int x;
  bind(unlambda(f), _1)(x);
}

Ugly? Kind of. But what's the case that wasn't solved? I'm too tired to figure out the minimal case, but I wrote something like this:

// Disclaimer: I don't have boost installed on this computer and haven't
// actually tried this particular code, so it might be bad. And I don't
// have a spell checker installed either.

template <class F> void traverse(region some_region, F traverse_func)
{
   foreach (point p, some_region)
      traverse_func(p.x, p.y);
}

template <class F>
void blend_point(buffer& dst, buffer src, int x, int y, F alpha_func)
{
   dst(x, y) = lerp(dst(x, y), src(x, y), alpha_func(x, y));
}

template <class F>
void blend(buffer& dst, buffer src, region some_region, F alpha_func)
{
   traverse(some_region, bind(blend_point, dst, src, _1, _2, alpha_func));
}

// then a few different blends
blend(dst, src1, some_region, _1 / constant((float) w));
blend(dst, src2, some_region, _2 / constant((float) h));
// etc ..., some with bind instead of lambda

Which didn't work. So I ended up finding out about the function composition, then I tried to use unlambda on stuff, which didn't help. I don't know exactly why it didn't help. Maybe it is a bug, or it just didn't work with the underlying stuff. I haven't looked that closely into things.

Anyway, by now you probably have figured out a much better fix for this long ago. I mean, if you think about it, it's fairly easy. You can just bind pointers to boost::bind objects/functors instead. And it works. Until you try it with a boost::lambda functor.

boost::lambda tries to be clever. It really has to, considering how the whole thing is built and what it does. I don't know the specific details on this specific cleverness of it, but if you use the address operator (&) on such a functor, you don't get a pointer to functor. You get the functor (or a copy, don't know exactly, didn't care enough to find out).

OK, I'm tired now, rant off. So... I heard the other day it's 2008. Which is kind of funny when you think about it.

Comments

Add a comment

Name:
URL:
Comment:
Markdown can be used, except for images and inline HTML. Max 2048 characters.
captcha. sorry.
Security string:
Enter the word shown in the image above.

©2004-2008 Ola Frid. Not powered by WordPress. Powered by me, PHP, SQLite, x-g, PHP Markdown and some other stuff. Times are CET/CEST.
* slog: Short for stupid log, a parody of blog, and a pun.