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;
}
}
```
1 Answer 1
Answers to your questions.
Have I defined a Monad object for C++?
Yes. Despite the name and its association with pure functional programming languages, it's not really that special. You can write monads in C as well. Since you have a data type that wraps a value, you have a way to construct that data type from a value, and you have functions that act on the wrapped value and returns that data type again, you have fulfilled all the obligations of the definition of a monad.
- Returning a
double
to manage partial ordering from the comp method.
Yes, that's unusual. I see you wanted a way to signal that two values are unequal but have no particular ordering, and I guess you chose double
because it has a NAN value? C++20 introduced std::partial_ordering
that you can use as the return value of a comparison function, which would be more appropriate. If you still want to rely on NAN, consider using float
instead of double
, as it will be a little more efficient.
- Returning
typeid(self).name();
as the default behavior.
If you are OK with _type_()
returning typeid(self).name()
, why do you need to be able to set a custom name? On the other hand, consider that the return value of std::type_info::name()
is implementation defined and not guaranteed to be unique. It could return "nothing"
for all types and that would be legal.
- Should default behaviour be invoked for classes that do not overwrite var friend functions.
I think that's the definition of default behaviour? However, the default behaviour should of course match what that function claims to do. So an _add_(const T& self, var other)
that just does return var()
is not what you want. If you cannot provide reasonable default behaviour, don't provide it at all.
- 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.
Concrete types and functions can be forward declared in the header, and their implementation can be put in source files. For templates, that normally doesn't work; they will only be instantiated when they are used, and at that point the whole definition must be available to the compiler. You can use explicit template instantiation, but then you need to know all types you will ever use in your program up front.
This doesn't scale
I am not sure what the intended use case is of var()
, but if you want it to be able to wrap any type and allow any operation on them, this approach just doesn't scale at all.
First, you only make available some functions for doing comparisons, arithmatic and list operations. What if you want to wrap a std::fstream
and allow read()
and write()
operations? You'd have to add more member functions to var
, to interface_type
, and to potentially many classes that derive from interface_type
.
Second, consider all the possible integer types, from 8 to 64 bits, signed and unsigned. If you want to allow adding any kind of integer to any other kind of integer, you would have to write 64 overloads for each binary operation. Add float
and double
, and you have to write 100 overloads.
-
\$\begingroup\$ thank you for the response! A few notes to your points: The class was original implicated before C++20. I think at this point I just need to revise it to meet that standard. That will address the double as a return to
comp
. Also, It isn't really meant to scale for all data types. Only the ones that have an overwritten class defined will scale. Though anything can be cast or copied from avar
. It was originally used to implement the data types in an interpreter. So beyond, holding a variable, casting it or copying it, the usefulness is limited. Thanks again! \$\endgroup\$StormCrow– StormCrow2023年03月30日 17:45:16 +00:00Commented Mar 30, 2023 at 17:45