Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2001-08-21 18:31:28


On Tuesday 21 August 2001 04:46, you wrote:
> Douglas Gregor wrote:
> > > If we're going to enshrine variants in C++,
> > > they should be done properly: by an extension
> > > to the core language.
> >
> > [snip 'adding features to core' discussion]
> >
> > With a little bit of
> > work I was able to come up with syntax similar to the above.
> >
> > variant<int, double, std::string, etc> v;
> > variant_switch(v)
> > .on<std::string>(print_string())
> > .on<int>(print_int())
> > .on_default(print_unknown());
> >
> > These variants are efficient and typesafe. Am I missing some key feature
> > that would require language support?
>
> I'm not sure. What does 'print_string()' print?
> Is this a method call on the object of the type given in the
> .on<T> pattern? How is this parsed by existing C++ compilers?

print_string() and print_int() are function objects that can be invoked given
an instance of std::string or int, respectively. Some definitions for the
sake of clarity:

struct print_string {
  void operator()(const std::string& s) { std::cout << s << std::endl; }
};

struct print_int {
  void operator()(int i) { std::cout << i << std::endl; }
};

So if the value currently held in v is of type std::string,
print_string::operator()(foo) would be executed, where "foo" is the
std::string contained in v.

The parsing of the construct:

variant_switch(v)
  .on<std::string>(print_string())
  .on<int>(print_int())
  .on_default(print_unknown());

variant_switch(v) returns an object of type variant_switch_stmt that contains
a reference to "v". It contains a member function:

  template<typename T, typename F>
  variant_switch_stmt on(F f)
  {
    if (v_reference.is<T>()) { // if v's current value is of type T
      f(v_reference.as<T>()); // retrieve a reference v's T and call f

      // make everything else a no-op from now on
    }
    // returning a variant_switch_stmt allows cascading
  }

Thus we're making calls to the "on" member function of temporary
variant_switch_stmt objects, each of which returns another
variant_switch_stmt object so that cascading can occur. "on" is responsible
for checking the type given by the user against the type of the value
currently in the variant.

[snip description of tags]
> So using the tagging hack, it is possible to convert the
> 'in principle wrong' type discrimination into
> 'in principle correct' tagged discrimination.

Yes, it's relatively easy to do.

> If you're using method calls, then, yes, the result
> would be typesafe (although I don't quite understand
> how this would work using existing C++ syntax).
>
> So:
> 1. It lacks compile time completion assurance.

This is not in my vocabulary :(. Does this refer to an inability to detect at
compile time whether all possible tags that could be used with an instance of
a variant have been accounted for in a case? e.g., given:

type t = X of int | Y of float | Z of float
match e with
  | X x -> print_int x
  | Y y -> print_float y

we should have a compile-time failure?

> 2. It would break the open/closed principle:
>
> you'd have to program a method for each match,
> in ML you can use a 'free function'. That's a very
> serious objection, since encapsulation is not
> desirable here.
>
> In fact, object orientation was _invented_ precisely
> to get around the incorrect use of variants for
> abstraction. But that doesn't mean ALL uses of
> variants are ill founded, it simply means that both
> mechanisms are needed. Most OO languages threw out
> all the good things in structured programming:
> variants and nesting, and the result is just as bad,
> if not worse, than the original paradigm -- as
> the reemergence of Functional programming is showing.
>
> Note: perhaps I misunderstood your intent.

It appears that way :(. I don't see how the open/closed principle comes into
play here. I hope that the above descriptions have made my intentions more
clear.

[reordered]
> > I might as well feed the flames a bit... I'm not sure I agree that there
> > is any reason to add variants into the core C++ language.
>
> > I'm not either. To be clear, I'm completely sure that
> a decent language requires the at least one representation
> of the fundamental and dual algebraic data types corresponding
> to products and sums: structs and discriminated unions.
> Some languages provide two products (tuples and records,
> using ordering and naming).
>
> However, it isn't clear if it is worth 'fixing' C++.

I'm not clear that it is even a fix, per se. I think that it is entirely
possible to emulate variants in C++ using templates. If so, I don't believe
there is any use in adding a redundant feature to the language.

        Doug


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk