2005-11-22 [長年日記]

[C++][Haskell] Maybe

HaskellのMaybeもどきをC++で書けるかなと思って書いてみました。本当にこんなんで良いのかどうかはかなり怪しいです。

#include <iostream>
#include <string>

namespace maybe {

template<class T>
class Maybe
{
public:
  Maybe(const T& t) : t_(t), fail_(false) {}
  Maybe() : fail_(true) {}

public:
  template<class Func>
  typename Func::result_type operator>=(Func func) const {
    return fail_ ? typename Func::result_type() : func(t_);
  }

private:
  T t_;
  bool fail_;

  template<class U>
  friend std::ostream& operator<<(std::ostream&, const Maybe<U>&);
};

template<class T>
Maybe<T> return_(const T& t) {
  return Maybe<T>(t);
}

template<class T>
Maybe<T> fail(const std::string& s) {
  return Maybe<T>();
}

template<class T>
std::ostream& operator<<(std::ostream& os, const Maybe<T>& maybe) {
  if (maybe.fail_)
    os << "Nothing" << std::endl;
  else
    os << "Just " << maybe.t_ << std::endl;
  return os;
}

}

本当は>>=をオーバーロードしたいところですが、>>=は右結合のために使いにくいので替わりに>=で代用してあります。また、returnはC++の予約語なのでreturn_にしてあります。

例えば、標準入力から整数を読み取って平方根を計算し、標準出力に出力するとこんな感じでしょうか。

#include <cmath>
#include "maybe.h"

namespace {

using namespace maybe;
using namespace std;

Maybe<int> read() {
  int n;
  cin >> n;
  return cin.fail() ? fail<int>("Could not read from cin.") : return_(n);
}

Maybe<double> sqrt_(int n) {
  return n < 0 ?
    fail<double>("Could not calculate sqrt of negative value.") :
    return_(sqrt(static_cast<double>(n)));
}

template<class T>
Maybe<T> print(T t) {
  cout << t << endl;
  return cout.fail() ? fail<T>("Could not write to cout.") : return_(t);
}

}

int main(int, char*[]) {
  cerr << (read() >= ptr_fun(&sqrt_) >= ptr_fun(&print<double>));
}

[C++][Haskell] Maybe (2)

Maybeを抽象基底クラスにして、JustとNothingを派生クラスにするという手もあるなぁ、というかそっちの方が意味的に近いかも、と思って作った第二段。

仮想関数はテンプレート化できないので、渡せる関数の型が厳密に決まってしまいます(前の奴は変換可能なら何でも大丈夫でした)。Boost.Functionあたりを使うとこの辺はもう少しうまくできそうです。

#include <cmath>
#include <iostream>
#include <memory>
#include <string>

namespace maybe2
{

template<class T>
class Maybe
{
public:
  typedef std::auto_ptr<Maybe<T> > (*Func)(T);

public:
  virtual ~Maybe() {}
  virtual std::auto_ptr<Maybe<T> > apply(Func func) const = 0;
  virtual void print(std::ostream& os) const = 0;
};

template<class T>
class Just : public Maybe<T>
{
public:
  Just(const T& t) : t_(t) {}
  virtual ~Just() {}
  virtual std::auto_ptr<Maybe<T> > apply(Func func) const {
    return func(t_);
  }
  virtual void print(std::ostream& os) const {
    os << "Just " << t_ << std::endl;
  }

private:
  T t_;
};

template<class T>
class Nothing : public Maybe<T>
{
public:
  Nothing() {}
  virtual ~Nothing() {}
  virtual std::auto_ptr<Maybe<T> > apply(Func func) const {
    return std::auto_ptr<Maybe<T> >(new Nothing());
  }
  virtual void print(std::ostream& os) const {
    os << "Nothing" << std::endl;
  }
};

template<class T>
std::auto_ptr<Maybe<T> > return_(const T& t) {
  return std::auto_ptr<Maybe<T> >(new Just<T>(t));
}

template<class T>
std::auto_ptr<Maybe<T> > fail(const std::string& s) {
  return std::auto_ptr<Maybe<T> >(new Nothing<T>());
}

template<class T>
std::auto_ptr<Maybe<T> > operator>=(const std::auto_ptr<Maybe<T> >& maybe,
                                    typename Maybe<T>::Func func) {
  return maybe->apply(func);
}

template<class T>
std::ostream& operator<<(std::ostream& os,
                         const std::auto_ptr<Maybe<T> >& maybe) {
  maybe->print(os);
  return os;
}

}

namespace {

using namespace maybe2;
using namespace std;

std::auto_ptr<Maybe<int> > read() {
  int n;
  cin >> n;
  return cin.fail() ? fail<int>("Could not read from cin.") : return_(n);
}

std::auto_ptr<Maybe<int> > sqrt_(int n) {
  return n < 0 ?
    fail<int>("Could not calculate sqrt of negative value.") :
    return_(static_cast<int>(sqrt(static_cast<double>(n))));
}

template<class T>
std::auto_ptr<Maybe<T> > print(T t) {
  cout << t << endl;
  return cout.fail() ? fail<T>("Could not write to cout.") : return_(t);
}

}

int main(int, char*[]) {
  cerr << (read() >= &sqrt_ >= &print<int>);
}

トップ «前の日記(2005-11-21) 最新 次の日記(2005-11-23)»