タグ付きバリアント型(代数的データ型)
タグ付きタプル型 - yohhoyの日記
タグ付きバリアント型があってもいいじゃない。
Boost.Variant では要素の型で要素にアクセスしますが、代わりにタグ経由でアクセスすることで、
- 要素の意味が明確になる。
- 複数の同じ型の要素に別の意味を与えることができる。
という利点が得られます。
同等のことは言語機能の union でも可能ですが、union を直接扱うのは面倒です。
ここでは Boost.Variant をラップしてタグ付きバリアント型としてみました。
使い方を示します。
準備
#include "named_variant.hpp" namespace nv = named_variants;
基本
// タグ宣言 struct I {}; struct D {}; // タグ付きバリアント // data Number = I Int | D Double using Number = nv::named_variant<I, int, D, double>; void example1() { // 初期化 Number n = nv::init<I>(42); // 格納されている型は nv::type() 関数で取得する if (nv::type(n) == typeid(I)) { // 値の取り出しは nv::get<タグ> で行う std::cout << "I " << nv::get<I>(n) << std::endl; } n = nv::init<D>(3.14); // パターンマッチ nv::match<void> ( n , nv::case_<I>([](int i) { std::cout << "I " << i << std::endl; }) , nv::case_<D>([](double d) { std::cout << "D " << d << std::endl; }) ); }
再帰データ型
struct Leaf {}; struct Branch {}; // 二分木 // data Tree a = Leaf a | Branch (Tree a) (Tree a) template <class T> using Tree = typename nv::make_recursive_named_variant < Leaf, T , Branch, std::pair<nv::recursive_named_variant_, nv::recursive_named_variant_> >::type; // 木の中に要素があるか調べる(深さ優先) template <class T, class TreeT> bool elem(T const &x, TreeT const &tree) { return nv::match<bool> ( tree , nv::case_<Leaf>([&](T const &y) { return x == y; }) , nv::case_<Branch>([&](std::pair<TreeT, TreeT> const &l_r) { return elem(x, l_r.first) || elem(x, l_r.second); }) ); } void example2() { Tree<int> const t = nv::init<Branch>(std::make_pair ( nv::init<Branch>(std::make_pair ( nv::init<Leaf>(1) , nv::init<Leaf>(2) )) , nv::init<Leaf>(3) )); if (elem(2, tree)) std::cout << "'tree' contains 2." << std::endl; }
Either
struct Left {}; struct Right {}; template <class T, class U> using Either = nv::named_variant<Left, T, Right, U>; void example3() { // 同じ型でも OK Either<std::string, std::string> const e = nv::init<Right>("Hello, world!"); nv::match<void> ( e , nv::case_<Left>([](std::string const &) {}) , nv::case_<Right>([](std::string const &s) { std::cout << s << std::endl; }) ); }
実装は https://gist.github.com/4054177 から。
named_variant<Name, Type, ...> は単に variant<fusion::pair<Name, Type>, ...> のエイリアスになっています。