Pure functional way of creating a std::optional counterpart

60 views Asked by At

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.

  1. 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;
}
  1. 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;
}
  1. 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;
}
  1. create the unsafe_get that 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

0

There are 0 answers