Skip to content

A monad is a monoid in the category of endofunctors, what about it

License

Notifications You must be signed in to change notification settings

DolphinGui/monads

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Monads

A monad is just a monoid in the category of endofunctors. Ok, more seriously a monad is a syntax akin to functions or variables. It's much easier to show rather than tell what a monad is.

#include "mtx/monads.hpp"

auto val = mtx::Value(123)
auto add1 = mtx::Monad([](int i){return i + 1;});
auto sum = val >>= add1;
auto sum_val = sum(); // sum_val == 124

As you can see, you first take a value and wrap it into a Value, then use the >>= operator to apply function Monad. When the Monad is applied by calling it like a function, the function is called on the value. In this case, it calls the lambda onto the value.

To state the obvious, monads are used to reprsent function composition logic. The Value monad is a syntactic sugar for Monad([]{return value;}), and is essentially a function in it of itself. The sum monad could be said to represent add1 ∘ val.

This library also contains various error handling monads. I think error handling can be relatively tersely expressed via monads rather than procedurally.

auto file = get_file(filename);
if(!a.has_value)
  return io_error;
auto json = to_json(read_file(*file, locale));
if(!json.has_value)
  return parse_error;
if(json->has_value(key))
  return not_found_error;
return json[key];

Here the error handling is interspersed between the business logic, making the code relatively messy.

try{
  auto file = get_file(filename);
  auto json = to_json(read_file(file, locale))
  return json[key]
} catch(const exception& e){
  return error(e); // or log and rethrow if that's what you want
}

Exceptions are significantly better in that all of the error handling logic is kept separately from the business logic. While variables are technically kept alive longer than strictly necessary, and arguably the try-catch block syntax is ugly, exceptions for many cases represent the most transparent and clean way to error handle. Exceptions even allow for easy error propogation.

The downside of exceptions is also arguably its upside. Many people don't actively think about exceptions, and don't write code that handles it well. Arguably this is a symptom of people not really error handling, and also the difficulty in writing code that is exception-safe or tolerant.

Functional programming tends not to use exceptions in favor of error handling, where monads make error checking much less painful.

// just pretend all of the functions are now monads
auto val = mtx::Value(filename) >>= get_file >>= mtx::Maybe
>>= read_file(locale) >>= to_json >>= mtx::Maybe >> getValue(key);
return val();

Here, the functions are "piped" to each other, with the Maybe monad representing conditional execution on whether or not the result is null. The val() returns an optional containing the value, although a Fallible monad works largely the same way for error codes instead of null;

auto val = mtx::Value(filename) >>= mtx::Trycatch >>= get_file >>= read_file(locale)
>>= to_json >>= getValue(key);
return val()

This is the exception based monad approach using the Trycatch monad. Note that this doesn't really have an analogue in most functional programming languages, as it uses exceptions to propogate the error. val() here also returns optional, although a TrycatchHandled monad exists that allows you to convert an exception into an error code.

For more usage, you can look at the unit tests in the tests directory. The monads were built using the transforming monad specialization returned by TransformMonad. The header file shows how TransformMonad does its magic.

About

A monad is a monoid in the category of endofunctors, what about it

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published