Have I defined a Monad object for C++? Below is a class called var
which I created years ago to implement a lambda calculus like interpreter in C++. The class defaults to a value of nothing
. If assigned a value, the behavior invoked during runtime is defined by the data type assigned to the value of var
. Through polymorphism the correct function is called. Within the function other instances of var
can be cast to the desired data type. Resulting in a null pointer if cast to the incorrect type.
After reading up on Monads, it looks like the var
class defined the behavior associated with Monads.
Also, I am very interested in any feedback on how to improve the class. Some aras of concern:
- Returning a
double
to manage partial ordering from thecomp
method. - Returning
typeid(self).name();
as the default behavior. - Should default behaviour be invoked for classes that do not overwrite
var
friend functions. - Is there any way to break up the implimentation from the header? Every time I try, there are always link issues were the associated type's function is missing.
Thank you in advance for any help offered!
Note: the entire code base is too big to be included here in its entirty; a repo, is here. Class var
was renamed from class let
in another incarnation, so as to be more meaningful in C++ code context. While it is a WIP, the entire class is below, to ensure the context of this question doesn't change over time.
namespace Olly {
class var {
struct interface_type;
public:
var();
template <typename T> var(T x);
friend std::ostream& operator<<(std::ostream& stream, var n);
template <typename T> std::shared_ptr<const T> cast() const; // Cast the object as an instance of the specified type.
template <typename T> T copy() const; // Get a copy of the objects as a specified type.
Text type() const; // The class generated type name.
Text cat() const; // The class generated category name.
bool is() const; // Is or is not the object defined.
void str(Text_Stream& out) const; // String representation of the object.
void repr(Text_Stream& out) const; // Recreatable text representation of an object.
operator bool() const;
double comp(var n) const; // Compare two objects. 0 = equality, > 0 = grater than, < 0 = less than, NAN = not same type.
bool eq(var n) const; // Equal to.
bool ne(var n) const; // Not equal to.
bool ge(var n) const; // Greater than equal to.
bool le(var n) const; // Less than equal to.
bool gt(var n) const; // Greater than.
bool lt(var n) const; // Less than.
bool operator==(var n) const;
bool operator!=(var n) const;
bool operator>=(var n) const;
bool operator> (var n) const;
bool operator<=(var n) const;
bool operator< (var n) const;
var b_and(var n) const; // Binary and.
var b_or(var n) const; // Binary or.
var b_xor(var n) const; // Binary exclusive or.
var b_neg() const; // Binary negation.
var operator&(var n) const;
var operator|(var n) const;
var operator^(var n) const;
var operator~() const;
var u_add() const; // Addition identity.
var u_neg() const; // Unary compliment.
var add(var n) const; // Addition.
var sub(var n) const; // Subtraction.
var mul(var n) const; // Multiplication.
var div(var n) const; // Division.
var mod(var n) const; // Modulus.
var operator+() const;
var operator-() const;
var operator+(var n) const;
var operator-(var n) const;
var operator*(var n) const;
var operator/(var n) const;
var operator%(var n) const;
var f_div(var n) const; // Floor divide.
var rem(var n) const; // Remainder.
var pow(var n) const; // Raise to the power of.
var root(var n) const; // Reduce to the root of.
bool has(var n) const; // Determine if an object has an element.
std::size_t size() const; // Size of an object.
var front() const; // Lead element of an object.
var back() const; // Last element of an object.
var place(var n) const; // Place an object as the front element.
var push(var n) const; // Place an object as the front element.
var next() const; // Remove the front element from an object.
var prev() const; // Remove the front element from an object.
var operator>>(std::size_t shift) const;
var operator<<(std::size_t shift) const;
var get(var key) const; // Get an element from a collection.
var set(var key, var val) const; // Set the value of an element in a collection.
var del(var key) const; // Delete an element from a collection.
var reverse() const; // Reverse the order of an object's elements.
std::size_t hash() const; // Get the hash of an object.
OP_CODE op_code() const;
bool is_nothing() const;
bool is_something() const;
Text help() const; // Define a string description of the object.
// TODO: add capture of arguments to pass to a function.
// TODO: figure out how to incorporate expression iteration.
private:
struct interface_type {
/********************************************************************************************/
//
// 'interface_type' Class Definition
//
// A simple interface description allowing redirection of the 'var' data type.
//
/********************************************************************************************/
virtual ~interface_type() {};
virtual operator bool() const = 0;
virtual Text _type() const = 0;
virtual Text _cat() const = 0;
virtual bool _is() const = 0;
virtual void _str(Text_Stream& out) const = 0;
virtual void _repr(Text_Stream& out) const = 0;
virtual double _comp(var n) const = 0;
virtual var _b_and(var n) const = 0;
virtual var _b_or(var n) const = 0;
virtual var _b_xor(var n) const = 0;
virtual var _b_neg() const = 0;
virtual var _u_add() const = 0;
virtual var _u_neg() const = 0;
virtual var _add(var n) const = 0;
virtual var _sub(var n) const = 0;
virtual var _mul(var n) const = 0;
virtual var _div(var n) const = 0;
virtual var _mod(var n) const = 0;
virtual var _f_div(var n) const = 0;
virtual var _rem(var n) const = 0;
virtual var _pow(var n) const = 0;
virtual var _root(var n) const = 0;
virtual bool _has(var n) const = 0;
virtual std::size_t _size() const = 0;
virtual var _front() const = 0;
virtual var _back() const = 0;
virtual var _place(var n) const = 0;
virtual var _push(var n) const = 0;
virtual var _next() const = 0;
virtual var _prev() const = 0;
virtual var _reverse() const = 0;
virtual var _get(var key) const = 0;
virtual var _set(var key, var val) const = 0;
virtual var _del(var key) const = 0;
virtual std::size_t _hash() const = 0;
virtual Text _help() const = 0;
virtual bool _is_nothing() const = 0;
virtual OP_CODE _op_code() const = 0;
};
template <typename T>
struct data_type : interface_type {
/******************************************************************************************/
//
// 'data_type' Class Definition
//
// The interface implementation of the 'interface_type' data type.
// Defining a shared const pointer of the data type passed to it.
//
/******************************************************************************************/
data_type(T val);
virtual ~data_type();
operator bool() const;
Text _type() const;
Text _cat() const;
bool _is() const;
void _str(Text_Stream& out) const;
void _repr(Text_Stream& out) const;
double _comp(var n) const;
var _b_and(var n) const;
var _b_or(var n) const;
var _b_xor(var n) const;
var _b_neg() const;
var _u_add() const;
var _u_neg() const;
var _add(var n) const;
var _sub(var n) const;
var _mul(var n) const;
var _div(var n) const;
var _mod(var n) const;
var _f_div(var n) const;
var _rem(var n) const;
var _pow(var n) const;
var _root(var n) const;
bool _has(var n) const;
std::size_t _size() const;
var _front() const;
var _back() const;
var _place(var n) const;
var _push(var n) const;
var _next() const;
var _prev() const;
var _get(var key) const;
var _set(var key, var val) const;
var _del(var key) const;
var _reverse() const;
std::size_t _hash() const;
Text _help() const;
bool _is_nothing() const;
OP_CODE _op_code() const;
T _data;
};
std::shared_ptr<const interface_type> _ptr;
};
/********************************************************************************************/
//
// 'nothing' Class Definition
//
// A basic class definition of the value of nothing. This is used within
// 'var' class implementation to return a result of nothing for results
// which have either conflicting types, or in some cases as the default to
// return unless overridden.
//
// This class also demonstrates the basic function methods that should be
// over written for proper object behavior.
//
/********************************************************************************************/
class nothing {
public:
friend Text _type_(const nothing& self);
friend bool _is_(const nothing& self);
friend double _comp_(const nothing& self, var n);
friend void _str_(Text_Stream& out, const nothing& self);
friend void _repr_(Text_Stream& out, const nothing& self);
friend bool _is_nothing_(const nothing& self);
};
/********************************************************************************************/
//
// Support Function Declarations
//
// These definitions add a few useful and some necessary support functions.
//
/********************************************************************************************/
Text str(var a); // Convert any 'var' to a Text.
Text repr(var a); // Convert any 'var' to a Text representation of the 'var'.
var pop_front(var& exp); // Remove and return the front element from an ordered expression.
var pop_back(var& exp); // Remove and return the back element from an ordered expression.
/********************************************************************************************/
//
// 'var' Class Function Default Template API.
//
// The following function templates define the over-ridable functions which
// may be used to tailor the behavior of any object held within a 'var'.
//
// Each function defined here defines the default behavior of each function
// which is invoked if a function is not overwritten for a defined class.
//
/********************************************************************************************/
template<typename T> /**** Type Name ****/
Text _type_(const T& self);
template<typename T>
Text _type_(const T& self) {
return typeid(self).name();
}
template<typename T> /**** Category Name ****/
Text _cat_(const T& self);
template<typename T>
Text _cat_(const T& self) {
return "uncategorized";
}
template<typename T> /**** Boolean Conversion ****/
bool _is_(const T& self);
template<typename T>
bool _is_(const T& self) {
return false;
}
template<typename T> /**** String Conversion ****/
void _str_(Text_Stream& out, const T& self);
template<typename T>
void _str_(Text_Stream& out, const T& self) {
// TODO: handle object based on stream availability.
//
constexpr bool n = is_streamable<std::stringstream, T>::value;
//if (n) {
// out << self;
//}
//else {
// out << "object<" << &self << "," << _type_(self) << ">";
//}
out << "object<" << &self << "," << _type_(self) << ">";
//out << self;
}
template<typename T> /**** String Representation ****/
void _repr_(Text_Stream& out, const T& self);
template<typename T>
void _repr_(Text_Stream& out, const T& self) {
out << str(nothing());
}
template<typename T> /**** Comparison Between Variables ****/
double _comp_(const T& self, var other);
template<typename T>
double _comp_(const T& self, var other) {
return NOT_A_NUMBER;
}
template<typename T> /**** Logical Conjunction ****/
var _b_and_(const T& self, var other);
template<typename T>
var _b_and_(const T& self, var other) {
return var();
}
template<typename T> /**** Logical Inclusive Disjunction ****/
var _b_or_(const T& self, var other);
template<typename T>
var _b_or_(const T& self, var other) {
return var();
}
template<typename T> /**** Logical Exclusive Disjunction ****/
var _b_xor_(const T& self, var other);
template<typename T>
var _b_xor_(const T& self, var other) {
return var();
}
template<typename T> /**** Negation ****/
var _b_neg_(const T& self);
template<typename T>
var _b_neg_(const T& self) {
return self;
}
template<typename T> /**** Unary Addition ****/
var _u_add_(const T& self);
template<typename T>
var _u_add_(const T& self) {
return var();
}
template<typename T> /**** Unary Compliment ****/
var _u_neg_(const T& self);
template<typename T>
var _u_neg_(const T& self) {
return var();
}
template<typename T> /**** Addition or Concatenation ****/
var _add_(const T& self, var other);
template<typename T>
var _add_(const T& self, var other) {
return var();
}
template<typename T> /**** Subtraction or Division ****/
var _sub_(const T& self, var other);
template<typename T>
var _sub_(const T& self, var other) {
return var();
}
template<typename T> /**** Multiplication ****/
var _mul_(const T& self, var other);
template<typename T>
var _mul_(const T& self, var other) {
return var();
}
template<typename T> /**** Division ****/
var _div_(const T& self, var other);
template<typename T>
var _div_(const T& self, var other) {
return var();
}
template<typename T> /**** Modulation ****/
var _mod_(const T& self, var other);
template<typename T>
var _mod_(const T& self, var other) {
return var();
}
template<typename T> /**** Floor Division ****/
var _f_div_(const T& self, var other);
template<typename T>
var _f_div_(const T& self, var other) {
return var();
}
template<typename T> /**** Remainder ****/
var _rem_(const T& self, var other);
template<typename T>
var _rem_(const T& self, var other) {
return var();
}
template<typename T> /**** Raise to Power of ****/
var _pow_(const T& self, var other);
template<typename T>
var _pow_(const T& self, var other) {
return var();
}
template<typename T> /**** Reduce to Power of ****/
var _root_(const T& self, var other);
template<typename T>
var _root_(const T& self, var other) {
return var();
}
template<typename T> /**** Check if an object has an element ****/
bool _has_(const T& self, var other);
template<typename T>
bool _has_(const T& self, var other) {
return false;
}
template<typename T> /**** Length Of ****/
std::size_t _size_(const T& self);
template<typename T>
std::size_t _size_(const T& self) {
return 0;
}
template<typename T> /**** Lead Element Of ****/
var _front_(const T& self);
template<typename T>
var _front_(const T& self) {
return var();
}
template<typename T> /**** Lead Element Of ****/
var _back_(const T& self);
template<typename T>
var _back_(const T& self) {
return var();
}
template<typename T> /**** Prepend Lead Element Of ****/
var _place_(const T& self, var other);
template<typename T>
var _place_(const T& self, var other) {
return var();
}
template<typename T> /**** Prepend Lead Element Of ****/
var _push_(const T& self, var other);
template<typename T>
var _push_(const T& self, var other) {
return var();
}
template<typename T> /**** Drop The Leading Element ****/
var _next_(const T& self);
template<typename T>
var _next_(const T& self) {
return var();
}
template<typename T> /**** Drop The Leading Element ****/
var _prev_(const T& self);
template<typename T>
var _prev_(const T& self) {
return var();
}
template<typename T> /**** Reverse The Elements Of ****/
var _reverse_(const T& self);
template<typename T>
var _reverse_(const T& self) {
return var();
}
template<typename T> /**** Retrieve A Selection From ****/
var _get_(const T& self, var other);
template<typename T>
var _get_(const T& self, var other) {
return var();
}
template<typename T> /**** Set A Selection Of ****/
var _set_(const T& self, var other, var val);
template<typename T>
var _set_(const T& self, var other, var val) {
return var();
}
template<typename T> /**** Remove A Selection From ****/
var _del_(const T& self, var other);
template<typename T>
var _del_(const T& self, var other) {
return var();
}
template<typename T> /**** Hash Value ****/
std::size_t _hash_(const T& self);
template<typename T>
std::size_t _hash_(const T& self) {
return DEFAULT_HASH_FUNCTION(repr(self));
}
template<typename T> /**** Return An Operation Code ****/
OP_CODE _op_code_(const T& self);
template<typename T>
OP_CODE _op_code_(const T& self) {
return OP_CODE::nothing_op;
}
template<typename T> /**** Determine If A Var Is Undefined ****/
bool _is_nothing_(const T& self);
template<typename T>
bool _is_nothing_(const T& self) {
return false;
}
template<typename T> /**** Return A Help String ****/
Text _help_(const T& self);
template<typename T>
Text _help_(const T& self) {
return "No object documentation available.";
}
std::ostream& operator<<(std::ostream& stream, var n) {
Text_Stream out;
out.copyfmt(stream);
n.str(out);
stream << out.str();
return stream;
}
/********************************************************************************************/
//
// 'nothing' Class Implementation
//
/********************************************************************************************/
Text _type_(const nothing& self) {
return "nothing";
}
bool _is_(const nothing& self) {
return false;
}
double _comp_(const nothing& self, var n) {
return NOT_A_NUMBER;
}
void _str_(Text_Stream& out, const nothing& self) {
out << "nothing";
}
void _repr_(Text_Stream& out, const nothing& self) {
out << "nothing";
}
bool _is_nothing_(const nothing& self) {
return true;
}
/********************************************************************************************/
//
// 'var' Class Implementation
//
/********************************************************************************************/
var::var() : _ptr(std::make_shared<data_type<nothing>>(nothing())) {
}
template <typename T>
var::var(T x) : _ptr(std::make_shared<data_type<T>>(x)) {
}
template <typename T>
std::shared_ptr<const T> var::cast() const {
auto p = std::dynamic_pointer_cast<const data_type<T>>(_ptr);
if (p == nullptr) {
return nullptr;
}
return std::shared_ptr<const T>(p, &(p->_data));
}
template <typename T>
T var::copy() const {
auto p = std::dynamic_pointer_cast<const data_type<T>>(_ptr);
if (p) {
return T{ *p };
}
return T{};
}
Text var::type() const {
return _ptr->_type();
}
Text var::cat() const {
return _ptr->_cat();
}
bool var::is() const {
return const_cast<interface_type*>(_ptr.get())->_is();
}
void var::str(Text_Stream& out) const {
_ptr->_str(out);
}
void var::repr(Text_Stream& out) const {
_ptr->_repr(out);
}
double var::comp(var n) const {
return _ptr->_comp(n);
}
bool var::eq(var n) const {
return (comp(n) == 0.0 ? true : false);
}
bool var::ne(var n) const {
return (comp(n) != 0.0 ? true : false);
}
bool var::ge(var n) const {
return (comp(n) >= 0.0 ? true : false);
}
bool var::le(var n) const {
return (comp(n) <= 0.0 ? true : false);
}
bool var::gt(var n) const {
return (comp(n) > 0.0 ? true : false);
}
bool var::lt(var n) const {
return (comp(n) < 0.0 ? true : false);
}
var var::b_and(var n) const {
return _ptr->_b_and(n);
}
var var::b_or(var n) const {
return _ptr->_b_or(n);
}
var var::b_xor(var n) const {
return _ptr->_b_xor(n);
}
var var::b_neg() const {
return _ptr->_b_neg();
}
var var::operator&(var n) const {
return b_and(n);
}
var var::operator|(var n) const {
return b_or(n);
}
var var::operator^(var n) const {
return b_xor(n);
}
var var::operator~() const {
return b_neg();
}
var var::u_add() const {
return _ptr->_u_add();
}
var var::u_neg() const {
return _ptr->_u_neg();
}
var var::add(var n) const {
return _ptr->_add(n);
}
var var::sub(var n) const {
return _ptr->_sub(n);
}
var var::mul(var n) const {
return _ptr->_mul(n);
}
var var::div(var n) const {
return _ptr->_div(n);
}
var var::mod(var n) const {
return _ptr->_mod(n);
}
var var::operator+() const {
return u_add();
}
var var::operator-() const {
return u_neg();
}
var var::f_div(var n) const {
return _ptr->_f_div(n);
}
var var::rem(var n) const {
return _ptr->_rem(n);
}
var var::pow(var n) const {
return _ptr->_pow(n);
}
var var::root(var n) const {
return _ptr->_root(n);
}
bool var::has(var n) const {
return _ptr->_has(n);
}
std::size_t var::size() const {
return _ptr->_size();
}
var var::front() const {
return _ptr->_front();
}
var var::back() const {
return _ptr->_back();
}
var var::place(var n) const {
return _ptr->_place(n);
}
var var::push(var n) const {
return _ptr->_push(n);
}
var var::next() const {
return _ptr->_next();
}
var var::prev() const {
return _ptr->_prev();
}
var var::operator>>(std::size_t shift) const {
var a{ *this };
while (shift--) {
a = a.next();
}
return a;
}
var var::operator<<(std::size_t shift) const {
var a{ *this };
while (shift--) {
a = a.prev();
}
return a;
}
var var::reverse() const {
return _ptr->_reverse();
}
var var::get(var n) const {
return _ptr->_get(n);
}
var var::set(var n, var val) const {
return _ptr->_set(n, val);
}
var var::del(var n) const {
return _ptr->_del(n);
}
std::size_t var::hash() const {
return _ptr->_hash();
}
OP_CODE var::op_code() const {
return _ptr->_op_code();
}
bool var::is_nothing() const {
return _ptr->_is_nothing();
}
bool var::is_something() const {
return !_ptr->_is_nothing();
}
Text var::help() const {
return _ptr->_help();
}
var::operator bool() const {
return is();
}
bool var::operator==(var n) const {
return eq(n);
}
bool var::operator!=(var n) const {
return ne(n);
}
bool var::operator>=(var n) const {
return ge(n);
}
bool var::operator> (var n) const {
return gt(n);
}
bool var::operator<=(var n) const {
return le(n);
}
bool var::operator< (var n) const {
return lt(n);
}
var var::operator+(var n) const {
return add(n);
}
var var::operator-(var n) const {
return sub(n);
}
var var::operator*(var n) const {
return mul(n);
}
var var::operator/(var n) const {
return div(n);
}
var var::operator%(var n) const {
return mod(n);
}
/********************************************************************************************/
//
// 'data_type' Class Implementation
//
/********************************************************************************************/
template <typename T>
var::data_type<T>::data_type(T val) : _data(std::move(val)) {
}
template<typename T>
var::data_type<T>::~data_type() {
}
template <typename T>
var::data_type<T>::operator bool() const {
return _is();
}
template <typename T>
std::size_t var::data_type<T>::_hash() const {
return _hash_(_data);
}
template <typename T>
Text var::data_type<T>::_type() const {
return _type_(_data);
}
template <typename T>
Text var::data_type<T>::_cat() const {
return _cat_(_data);
}
template <typename T>
bool var::data_type<T>::_is() const {
return _is_(_data);
}
template <typename T>
void var::data_type<T>::_str(Text_Stream& out) const {
_str_(out, _data);
}
template <typename T>
void var::data_type<T>::_repr(Text_Stream& out) const {
_repr_(out, _data);
}
template <typename T>
double var::data_type<T>::_comp(var n) const {
return _comp_(_data, n);
}
template <typename T>
var var::data_type<T>::_b_and(var n) const {
return _b_and_(_data, n);
}
template <typename T>
var var::data_type<T>::_b_or(var n) const {
return _b_or_(_data, n);
}
template <typename T>
var var::data_type<T>::_b_xor(var n) const {
return _b_xor_(_data, n);
}
template <typename T>
var var::data_type<T>::_b_neg() const {
return _b_neg_(_data);
}
template<typename T>
inline var var::data_type<T>::_u_add() const {
return _u_add_(_data);
}
template<typename T>
inline var var::data_type<T>::_u_neg() const {
return _u_neg_(_data);
}
template <typename T>
var var::data_type<T>::_add(var n) const {
return _add_(_data, n);
}
template <typename T>
var var::data_type<T>::_sub(var n) const {
return _sub_(_data, n);
}
template <typename T>
var var::data_type<T>::_mul(var n) const {
return _mul_(_data, n);
}
template <typename T>
var var::data_type<T>::_div(var n) const {
return _div_(_data, n);
}
template <typename T>
var var::data_type<T>::_mod(var n) const {
return _mod_(_data, n);
}
template <typename T>
var var::data_type<T>::_f_div(var n) const {
return _f_div_(_data, n);
}
template <typename T>
var var::data_type<T>::_rem(var n) const {
return _rem_(_data, n);
}
template <typename T>
var var::data_type<T>::_pow(var n) const {
return _pow_(_data, n);
}
template <typename T>
var var::data_type<T>::_root(var n) const {
return _root_(_data, n);
}
template <typename T>
bool var::data_type<T>::_has(var n) const {
return _has_(_data, n);
}
template <typename T>
std::size_t var::data_type<T>::_size() const {
return _size_(_data);
}
template <typename T>
var var::data_type<T>::_front() const {
return _front_(_data);
}
template <typename T>
var var::data_type<T>::_back() const {
return _back_(_data);
}
template <typename T>
var var::data_type<T>::_place(var n) const {
return _place_(_data, n);
}
template <typename T>
var var::data_type<T>::_push(var n) const {
return _push_(_data, n);
}
template <typename T>
var var::data_type<T>::_next() const {
return _next_(_data);
}
template <typename T>
var var::data_type<T>::_prev() const {
return _prev_(_data);
}
template <typename T>
var var::data_type<T>::_reverse() const {
return _reverse_(_data);
}
template <typename T>
var var::data_type<T>::_get(var key) const {
return _get_(_data, key);
}
template <typename T>
var var::data_type<T>::_set(var key, var val) const {
return _set_(_data, key, val);
}
template <typename T>
var var::data_type<T>::_del(var key) const {
return _del_(_data, key);
}
template <typename T>
Text var::data_type<T>::_help() const {
return _help_(_data);
}
template <typename T>
bool var::data_type<T>::_is_nothing() const {
return _is_nothing_(_data);
}
template <typename T>
OP_CODE var::data_type<T>::_op_code() const {
return _op_code_(_data);
}
/********************************************************************************************/
//
// Basic Primitive Implementations
//
/********************************************************************************************/
Text str(var a) {
/*
Convert a 'var' to its string representation.
*/
Text_Stream stream;
stream << std::boolalpha;
if (a.type() == "format") {
/*
The 'format' data type must be printed using
its string representation, else it would only
impart its formatting to the stream instead of
being printed to it.
*/
a.repr(stream);
}
else {
a.str(stream);
}
return stream.str();
}
Text repr(var a) {
/*
Convert a 'var' to its representation as a string.
*/
Text_Stream stream;
stream << std::boolalpha;
a.repr(stream);
return stream.str();
}
var pop_front(var& exp) {
var a = exp.front();
exp = exp.next();
return a;
}
var pop_back(var& exp) {
var a = exp.back();
exp = exp.prev();
return a;
}
}
```