|
Boost : |
From: Brian McNamara (lorgon_at_[hidden])
Date: 2003-10-22 11:37:19
On Tue, Oct 21, 2003 at 03:56:26PM -0400, Douglas Gregor wrote:
> On Tuesday 21 October 2003 02:34 am, Brian McNamara wrote:
> > Can we create a lightweight tag mechanism?
>
> Why not derive the tags from the representation, e.g.,
>
> struct TagName : public Representation
> { /* sure, constructors are annoying here */ };
>
> > Then Doug's example could be realized as
>
> typedef int Literal;
> template<typename V> struct Add : public tuple<V, V> {};
> template<typename V> struct Sub : public tuple<V, V> {};
> template<typename V> struct Mul : public tuple<V, V> {};
> template<typename V> struct Div : public tuple<V, V> {};
You need to redefine constructors for those 4 types (as you know).
> typedef recursive_variant<
> Literal,
> Add<recursive_variant_>,
> Sub<recursive_variant_>,
> Mul<recursive_variant_>,
> Div<recursive_variant_>
> >::type Expr;
>
> int eval( Expr e ) {
> return switch_<int>( e )
> |= case<Add>( _1+_2 )
> |= case<Sub>( _1-_2 )
> |= case<Mul>( _1*_2 )
> |= case<Div>(
> if_then_else_return(_2==0,throw_exception("div by 0"),_1-_2) )
> |= case<Literal>( _1 )
> ;
> }
There are a number of other minor problems which have accumulated.
Here's a new version which is hopefully bug-free:
----------------------------------------------------------------------
typedef int Literal;
template<typename V> struct Add_ : public tuple<V, V> {};
template<typename V> struct Sub_ : public tuple<V, V> {};
template<typename V> struct Mul_ : public tuple<V, V> {};
template<typename V> struct Div_ : public tuple<V, V> {};
typedef recursive_variant<
Literal,
Add_<recursive_variant_>,
Sub_<recursive_variant_>,
Mul_<recursive_variant_>,
Div_<recursive_variant_>
>::type Expr;
typedef Add_<Expr> Add;
typedef Sub_<Expr> Sub;
typedef Mul_<Expr> Mul;
typedef Div_<Expr> Div;
int eval( Expr e ) {
return switch_<int>( e )
|= case<Add>( eval(_1)+eval(_2) )
|= case<Sub>( eval(_1)-eval(_2) )
|= case<Mul>( eval(_1)*eval(_2) )
|= case<Div>(
if_then_else_return(eval(_2)==0,
throw_exception("div by 0"),
eval(_1)/eval(_2) )
|= case<Literal>( _1 )
;
}
----------------------------------------------------------------------
That almost works "out of the box"; the only thing else is that stuff
like
|= case<Add>( eval(_1)+eval(_2) )
isn't right, because the proposed switch_ construct would want it to be
// pseudocode for clairty
|= case<Add>( lambda(a){ eval( get<1>(a) )+eval( get<2>(a) ) } )
Right?
Wow, and it just occurred to me that there is a general and powerful
abstraction lurking here, namely:
f( taf ) = lambda( tuple<X,Y> t ){ taf( get<1>(t), get<2>(t) ) }
Here "taf" means "two argument function". I am not sure what to name
"f". Clearly this generalizes to n-ary tuples.
Oh, duh. This is what Haskell calls "uncurry". Wow! Ok, so the long
and the short of it is that I should be writing
|= case<Add>( uncurry( eval(_1)+eval(_2) ) )
and now things totally work, right out of the box. Cooooooool.
> Those extra V's are particularly annoying, aren't they? However, I will note
Annoying, but clever. :)
> > It is also not clear to me that this will ever be lightweight enough to
> > be useful.
>
> I'm not sure. It would take me a lot more code to write the same expression
> tree for my example as simple classes, and I wouldn't get the benefits of a
> smart typeswitch. Well, okay, we'd need some constructor functions like this
> to make the above useful:
>
> Add<Expr> add(const Expr& x, const Expr& y)
> { return Add<Expr>(make_tuple(x, y));
Actually, the "redefined constructors" I mentioned at the top of this
messages suffice for this job:
Expr x, y;
...
Add( x, y ) // voila!
> > To sum up:
> > - I like your original idea
> > - In theory, I like pattern matching on values even more than I like
> > pattern matching on types
>
> I'm not sure if I care all that much about matching on values. I do, however,
> think that using tags can shorten code a bit.
I just realized I didn't communicate what I meant here. By "matching
on values", I meant being able to use, e.g., "_1" inside the "Add" case
to mean the left-hand expression tree. I did _not_ intend to imply
that I wanted to be able to have "case 0", "case 1", and "case n" arms,
for example.
So I think we're mostly on the same page.
This is great stuff!
-- -Brian McNamara (lorgon_at_[hidden])
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk