В
статье в LinuxJournal приводится оригинальная интерпретация одной из концепций ФП — монад. Но все примеры были на языках, поддерживающих замыкания. В ожидании С++0х со встроенной их поддержкой попробовал переписать примеры "руками". Извиняюсь за обилие кода, меньше не получилось
#include <iostream>
template <typename TValue>
class CMonad;
template <typename TValue>
class CMonad
{
public:
typedef CMonad<TValue> TRetInst;
typedef TValue TArgInst;
protected:
TValue m_value;
CMonad() {}
public:
struct CMyFunctor
{
virtual TRetInst * apply(const TArgInst &) = 0;
};
CMonad(const TValue &_value) : m_value(_value) {}
virtual TRetInst * pass(CMyFunctor * pfn)
{
return pfn->apply(m_value);
}
virtual ~CMonad() {}
virtual void Print() const = 0;
};
template <class TValue>
struct IArithmetic
{
typedef CMonad<TValue> TRetInst;
template <typename TInstance>
class CAddFunctor : public CMonad<TValue>::CMyFunctor
{
const TValue & m_value;
public:
CAddFunctor(const TValue &_value) : m_value(_value) {}
virtual TRetInst * apply(const TValue &_value)
{
return new TInstance(m_value + _value);
}
};
template <template <typename> class CApplication, typename TInstance>
class CMyBinder : public CMonad<TValue>::CMyFunctor
{
TRetInst * m_value;
public:
CMyBinder(TRetInst * _value) : m_value(_value) {}
virtual TRetInst * apply(const TValue &_value)
{
CApplication<TInstance> app(_value);
return m_value->pass(&app);
}
};
template <typename TInstance>
IArithmetic<TValue> * Add(TInstance &rhs)
{
CMyBinder<CAddFunctor, TInstance> app(dynamic_cast<TRetInst *>(&rhs));
return dynamic_cast<IArithmetic<TValue> *>(dynamic_cast<CMonad<TValue>*>(this)->pass(&app));
}
virtual ~IArithmetic() {}
};
template <typename TValue, typename TInstance>
class CMaybe : public CMonad<TValue>
{
bool m_bNone;
public:
struct None {};
static None & getNone()
{
static None none;
return none;
}
typedef CMonad<TValue> IParent;
CMaybe(const TValue &_value) : IParent(_value), m_bNone(false) {}
CMaybe(const None &) : m_bNone(true) {}
virtual typename IParent::TRetInst * pass(typename IParent::CMyFunctor * pfn)
{
if (m_bNone)
{
return new TInstance(getNone());
}
return IParent::pass(pfn);
}
virtual void Print() const
{
if (m_bNone)
{
std::cout << "Maybe(none)" << std::endl;
}
else
{
std::cout << "Maybe(" << this->m_value << ")" << std::endl;
}
}
};
template <typename TValue, typename TInstance>
class CMaybe2 : public CMonad<TValue>
{
bool m_bNone;
public:
struct None {};
static None & getNone()
{
static None none;
return none;
}
typedef CMonad<TValue> IParent;
CMaybe2(const TValue &_value) : IParent(_value), m_bNone(false) {}
CMaybe2(const None &) : m_bNone(true) {}
virtual typename IParent::TRetInst * pass(typename IParent::CMyFunctor * pfn)
{
if (m_bNone)
{
return new TInstance(TValue());
}
return IParent::pass(pfn);
}
virtual void Print() const
{
std::cout << "Maybe(" << this->m_value << ")" << std::endl;
}
};
template <typename T>
struct CArithmeticMaybe : public CMaybe<T, CArithmeticMaybe<T> >, public IArithmetic<T>
{
typedef CMaybe<T, CArithmeticMaybe<T> > CMaybeParent;
CArithmeticMaybe(const typename CMaybeParent::None &none) : CMaybeParent(none) {}
CArithmeticMaybe(const T &_value) : CMaybeParent(_value) {}
};
template <typename T>
struct CArithmeticMaybe2 : public CMaybe2<T, CArithmeticMaybe2<T> >, public IArithmetic<T>
{
typedef CMaybe2<T, CArithmeticMaybe2<T> > CMaybeParent;
CArithmeticMaybe2(const typename CMaybeParent::None &none) : CMaybeParent(none) {}
CArithmeticMaybe2(const T &_value) : CMaybeParent(_value) {}
};
typedef CArithmeticMaybe<int> CMaybeInt;
typedef CArithmeticMaybe2<int> CMaybeInt2;
CMaybeInt n1(5), n2(6), n3(CMaybeInt::getNone());
CMaybeInt2 m1(5), m2(6), m3(CMaybeInt2::getNone());
int main(int argc, char* argv[])
{
dynamic_cast<CMonad<int>*>(n1.Add(n2)->Add(n2))->Print();
dynamic_cast<CMonad<int>*>(n1.Add(n2)->Add(n3))->Print();
std::cout << "--------------------" << std::endl;
dynamic_cast<CMonad<int>*>(m1.Add(m2)->Add(m2))->Print();
dynamic_cast<CMonad<int>*>(m1.Add(m2)->Add(m3))->Print();
dynamic_cast<CMonad<int>*>(m3.Add(m2)->Add(m1))->Print();
// все ухищрения с dynamic_cast ради вот такой возможности
// связывать монады разных типов
std::cout << "--------------------" << std::endl;
dynamic_cast<CMonad<int>*>(m1.Add(n2)->Add(m3))->Print();
dynamic_cast<CMonad<int>*>(m1.Add(m2)->Add(n2))->Print();
dynamic_cast<CMonad<int>*>(m3.Add(n2)->Add(m2))->Print();
return 0;
}
Что мне не нравится:
1. Приходится постоянно выносить действительное имя класса в качестве шаблонного параметра, чтобы иметь возможность создавать объект нужного класса.
2. Чтобы связывать монады разных типов, приходится постоянно использовать dynamic_cast.
Профи буста, подскажите, пожалуйста, можно ли добиться аналогичного эффекта в статике? А главное, как это сделать...
PS. Про утечку памяти можно мне не объяснять, я и сам знаю, где она появляется, и как это лечится.
PPS. Подозреваю, что изобрёл велоспед, и если это так, не пинайте слишком сильно
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Курица — это инструмент, с помощью которого одно яйцо производит другие.