Boost logo

Proto :

Subject: Re: [proto] : Proto transform with state
From: Eric Niebler (eric_at_[hidden])
Date: 2010-11-18 15:31:22


On 11/18/2010 1:45 PM, Thomas Heller wrote:
> Eric Niebler <eric_at_...> writes:
>> On 11/18/2010 6:09 AM, Thomas Heller wrote:
>>> Here goes the renumbering example:
>>> http://codepad.org/K0TZamPb
> <snip>
>> Unfortunately, this doesn't actually solve the reevaluation problem ...
>> it just hides it.
>
> Yes, exactly.
>
>> If "_a" gets replaced eagerly everywhere with
>> "Renumber(_, second(proto::_state))", then that transform will actually
>> get run every time. What I want is a way to evaluate the transform,
>> store the result in a temporary, and have "_a" refer to that temporary.
>
> Yes, I thought of doing this ... but could not find a solution. The question is,
> do we really need this behaviour? Is replacement not enough?
> Could you make up a usecase? I can not think of one ... Regarding that counting
> example ... it can be solved by this pair hack.

The Renumber example is itself a use-case. I had to hack around the lack
of this feature with the silly type_of transform:

  struct RenumberFun
    : proto::fold<
          _
        , make_pair(fusion::vector0<>(), proto::_state)
        , make_pair(
              push_back(
                  first(proto::_state)
                , first(Renumber(_, second(proto::_state)))
              )
            , type_of<second(Renumber(_, second(proto::_state))) >
          )
>
  {};

That should simply be:

  struct RenumberFun
    : proto::fold<
          _
        , make_pair(fusion::vector0<>(), proto::_state)
        , let<
              _a(Renumber(_, second(proto::_state)))
            , make_pair(
                  push_back(first(proto::_state), first(_a))
                , second(_a)
              )
>
>
  {};

See? If Renumber gets invoked once and the result "stored" in _a, then
we can just do first(_a) and then second(_a) and not incur any runtime
overhead. type_of was a hack that took advantage of the fact that the
result of second(_a) contains no interesting runtime state (it's just a
MPL int_). There will be times when that's not the case.

>> It's REALLY hard. The let context needs to be bundled with the Expr,
>> State, or Data parameters somehow, but in a way that's transparent. I
>> don't actually know if it's possible.
>
> Very hard ... yeah. I am thinking that we can maybe save these variables in the
> transform?

I'm thinking we just stuff it into the Data parameter. We have a
let_scope template that is effectively a pair containing:

1. The user's original Data, and
2. A Fusion map from local variables (_a) to values.

The let transform evaluates the bindings and stores the result in the
let_scope's Fusion map alongside the user's Data. We pass the let_scope
as the new Data parameter. _a is itself a transform that looks up the
value in Data's Fusion map. The proto::_data transform is changed to be
aware of let_scope and return only the original user's Data. This can
work. We also need to be sure not to break the new
proto::external_transform.

The problems with this approach as I see it:

1. It's not completely transparent. Custom primitive transforms will see
that the Data parameter has been monkeyed with.

2. Local variables like _a are not lexically scoped. They are, in fact,
dynamically scoped. That is, you can access _a outside of a let<>
clause, as long as you've been called from within a let clause.

Might be worth it. But as there's no pressing need, I'm content to let
this simmer. Maybe we can think of something better.

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com



Proto list run by eric at boostpro.com