solodon4 / Mach7

Functional programming style pattern-matching library for C++
Other
1.28k stars 79 forks source link

adapt_boost_variant: how to say 'any sub-type of a variant'? #31

Open akrzemi1 opened 8 years ago

akrzemi1 commented 8 years ago

I am trying to do a type switch on two variants, as per the example below. My intention is to match the case where the left-hand object 'tank' has the sub-type TankC and the right-hand side object 'load' has any type. I managed to achieve the effect by listing two labels one immediately after the other, but I guess there must be a more direct way of expressing this?

#include <boost/variant.hpp>
#include <Mach7/code/type_switchN-patterns-xtl.hpp>
#include <Mach7/code/adapters/boost/adapt_boost_variant.hpp>
#include <Mach7/code/patterns/constructor.hpp>

struct TankA {};
struct TankB {};
struct TankC {};

struct Load1 {};
struct Load2 {};

typedef boost::variant<TankA, TankB, TankC> Tank;
typedef boost::variant<Load1, Load2> Load;

int interact(Tank const& tank, Load const& load)
{
  using mch::C;

  Match (tank, load)
  {
    Case (C<TankA>(), C<Load1>())
      return 61;
    Case (C<TankA>(), C<Load2>())
      return 62;
    Case (C<TankB>(), C<Load1>())
      return 71;
    Case (C<TankB>(), C<Load2>())
      return 72;
    Case (C<TankC>(), C<Load1>()) //
    Case (C<TankC>(), C<Load2>()) // I want these two cases to be one
      return 0;
    Otherwise()
      return -1;
  }
  EndMatch
}

int main()
{
  assert (interact(TankC(), Load2()) == 0);
}
solodon4 commented 8 years ago

Hi Andrzej,

Read the comment for XTL_FALL_THROUGH in config.h. Right now it is enabled, but it shouldn't and I should have removed that functionality altogether long time ago. The problem is that the static type of match0, match1 etc. is different in each clause, so fall-through doesn't really make sense, not in a library at least. In a language solution you may argue that their type should be the join of their types, but that's a whole different discussion. You don't really get a fall-through in your case, you get a second clause checked after the first one.

Yuriy

On Mon, Feb 8, 2016 at 3:50 PM, Andrzej Krzemieński < notifications@github.com> wrote:

I am trying to do a type switch on two variants, as per the example below My intention is to match the case where the left-hand object 'tank' has the sub-type TankC and the right-hand side object 'load' has any type I managed to achieve the effect by listing two labels one immediately after the other, but I guess there must be a more direct way of expressing this?

include <boost/varianthpp>

include <Mach7/code/type_switchN-patterns-xtlhpp>

include <Mach7/code/adapters/boost/adapt_boost_varianthpp>

include <Mach7/code/patterns/constructorhpp>

struct TankA {};struct TankB {};struct TankC {}; struct Load1 {};struct Load2 {}; typedef boost::variant<TankA, TankB, TankC> Tank;typedef boost::variant<Load1, Load2> Load; int interact(Tank const& tank, Load const& load) { using mch::C;

Match (tank, load) { Case (C(), C()) return 61; Case (C(), C()) return 62; Case (C(), C()) return 71; Case (C(), C()) return 72; Case (C(), C()) // Case (C(), C()) // I want these two cases to be one return 0; Otherwise() return -1; } EndMatch } int main() { assert (interact(TankC(), Load2()) == 0); }

— Reply to this email directly or view it on GitHub https://github.com/solodon4/Mach7/issues/31.

akrzemi1 commented 8 years ago

Ok, so my example doesn't do what I intended. But my question still remains: is there a way to say "match TankC on the left-hand side and match anything on the right-hand side"? Like:

Case (C<TankC>(), match_anything)

The use case is this: in case I get TankC on the left-hand side, the type (and its value) on the other side is irrelevant and not needed to compute the result. In that case I would never need to access match1.

solodon4 commented 8 years ago

of course, just use the wildcard pattern _

include <mach7/patterns/primitive.hpp>

using mch::_;

Case(C(), _) ...

Alternatively, Case allows specifying less arguments than subjects in which case all missing patterns are assumed to be wildcard and match unconditionally.

Btw, I started moving headers, just haven't finished yet.

Yuriy

On Tue, Feb 9, 2016 at 2:37 AM, Andrzej Krzemieński < notifications@github.com> wrote:

Ok, so my example doesn't do what I intended. But my question still remains: is there a way to say "match TankC on the left-hand side and match anything on the right-hand side"? Like:

Case (C(), match_anything)

The use case is this: in case I get TankC on the left-hand side, the type (and its value) on the other side is irrelevant and not needed to compute the result. In that case I would never need to access match1.

— Reply to this email directly or view it on GitHub https://github.com/solodon4/Mach7/issues/31#issuecomment-181808855.

akrzemi1 commented 8 years ago

The following program does not compile, even after using the wildcard pattern. I get an error about ambiguous class template instantiation (xtl::is_subtype). Omitting the second argument in Case() also does not work (static_assert fires). Note that I am using the macros defined in the header type_switchN-patterns-xtl.hpp - or am I supposed to use a different one?

-- Andrzej

#include <boost/variant.hpp>
#include <Mach7/code/type_switchN-patterns-xtl.hpp>
#include <Mach7/code/adapters/boost/adapt_boost_variant.hpp>
#include <Mach7/code/patterns/constructor.hpp>
#include <mach7/code/patterns/primitive.hpp>

struct TankA {};
struct TankB {};
struct TankC {};

struct Load1 {};
struct Load2 {};

typedef boost::variant<TankA, TankB, TankC> Tank;
typedef boost::variant<Load1, Load2> Load;

int interact(Tank const& tank, Load const& load)
{
  using mch::C;
  using mch::_;

  Match(tank, load)
  {
    Case (C<TankA>(), C<Load1>())
      return 61;
    Case (C<TankA>(), C<Load2>())
      return 62;
    Case (C<TankB>(), C<Load1>())
      return 71;
    Case (C<TankB>(), C<Load2>())
      return 72;
    Case (C<TankC>(), _)
      return 0;
    Otherwise()
      return -1;
  }
  EndMatch
}

int main()
{
  assert (interact(TankC(), Load1()) == 0);
}
solodon4 commented 8 years ago

For variant, that is the right header, other headers would only assume subclassing. I'll have a look at this tonight - I probably need to add a couple more subtyping specializations.

On Tue, Feb 9, 2016 at 12:28 PM, Andrzej Krzemieński < notifications@github.com> wrote:

The following program does not compile, even after using the wildcard pattern. I get an error about ambiguous class template instantiation ( xtl::is_subtype). Omitting the second argument in Case() also does not work (static_assert fires). Note that I am using the macros defined in the header type_switchN-patterns-xtl.hpp - or am I supposed to use a different one?

  • Andrzej

include <boost/variant.hpp>

include <Mach7/code/type_switchN-patterns-xtl.hpp>

include <Mach7/code/adapters/boost/adapt_boost_variant.hpp>

include <Mach7/code/patterns/constructor.hpp>

include <mach7/code/patterns/primitive.hpp>

struct TankA {}; struct TankB {}; struct TankC {};

struct Load1 {}; struct Load2 {};

typedef boost::variant<TankA, TankB, TankC> Tank; typedef boost::variant<Load1, Load2> Load;

int interact(Tank const& tank, Load const& load) { using mch::C; using mch::_;

Match(tank, load) { Case (C(), C()) return 61; Case (C(), C()) return 62; Case (C(), C()) return 71; Case (C(), C()) return 72; Case (C(), _) return 0; Otherwise() return -1; } EndMatch }

int main() { assert (interact(TankC(), Load1()) == 0); }

— Reply to this email directly or view it on GitHub https://github.com/solodon4/Mach7/issues/31#issuecomment-182047270.

solodon4 commented 8 years ago

Code compiles with Clang, so I guess it is an MSVC-specific issue. Not sure yet why.

As to static_assert, I remember now I added it deliberately because some of the patterns might have commas in them without being wrapped into ( and ). Preprocessor would then treat them as separate arguments, so it was more useful to have the compiler assert when the number of patterns was not matching the number of subjects.

akrzemi1 commented 8 years ago

Ok. Can you give me some background then? Sub-type matching works when the correct unambiguous specialization of subtype_dynamic_cast_impl is found (and returns a non-null pointer). But apparently there is no such specialization for mch::wildcard. So at some point there must be a decision that for mch::wildcard we do not use the XTL mechanism. Can you tell me where it is?

akrzemi1 commented 8 years ago

I am also experiencing similar problems with GCC.