I'm working on a pure functional library for c++ and I met a design problem. I was creating the monad Option. This is my implementation
namespace ns {
struct _NoValue_t {};
const _NoValue_t _NoValue;
template<typename T>
class Option {
bool _has_value;
std::unique_ptr<T> _value;
Option(const T& value) :
_has_value(true), _value(new T(value)) {}
Option(T&& value) :
_has_value(true), _value(new T(std::move(value))) {}
Option(const _NoValue_t&) :
_has_value(false), _value(nullptr) {};
public:
Option(Option<T>&& other) :
_has_value(other._has_value), _value(other._value)
{
_has_value = false;
}
const Option<T>& operator=(Option<T>&& other) {
_has_value = other._has_value;
_value = std::move(other._value);
other._value = nullptr;
other._has_value = false;
return *this;
}
bool has_value() const {
return _has_value;
};
const T& value_else(const T& t) const {
return _has_value?(*_value):t;
}
static Option<T> some(const T& value) {
return Option<T>(value);
}
static Option<T> some(T&& value) {
return Option<T>(std::move(value));
}
static Option<T> none() {
return Option<T>(_NoValue);
}
}; //class Option
}
The only way of retrieving the value is trough value_else, the problem is that in case the user of the library has no value to give as default value, he/she should create it, but this operation may have a large cost.
I looked on the web and searched how other languages that mix oop and fp (kotlin, scala, f#, sometimes java) handle errors in a functional way, The solution listed below comes from this research.
- Throw an exception if there is no value, just like the
std::optional, I'd like to ignore this solution to keep code pure functional
const T& value() const {
if(!this->_has_value)
throw no_such_value_exception("if !option.has_value must not retrieve value");
return *_value;
}
- Pass a function that generate a default value instead of just the value, in this way unnecessary object creation would be avoided, but the result value can't be passed as reference (at least i guess so, if there is a way please tell me)
template<typename Generator>
T get(Generator&& generator) const {
if(!this->_has_value)
return generator();
else
return *this->_value;
}
- Return a default object if there is no value, but some objects may have no default constructor, this solution has the same problem as the n° 2
T get() const {
if(!this->_has_value)
return T();
else
return *this->_value;
}
- create the
unsafe_getthat at least explicit it's dangerous
const T& get() const {
return *this->_value;
}
Hope I made myself clear. I would also like to know if you can see unsafe code, logic error or anything else that you may think it's wrong. Also I 'm not sure if I'm give to much importance at how much distracted will be the programmer that will use this library. Waiting your answer and thank you in advance