Skip to content

Instantly share code, notes, and snippets.

@codebje
Last active August 29, 2015 14:22
Show Gist options
  • Save codebje/ad3059ea01214d2f6b7c to your computer and use it in GitHub Desktop.
Save codebje/ad3059ea01214d2f6b7c to your computer and use it in GitHub Desktop.
A badly written monad in C
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OPTIONAL_INIT(t) typedef struct optional_ ## t { short isPresent; t value; } optional_ ## t
#define OPTIONAL(t) optional_ ## t
static short _optional_empty = 0;
struct optional_any { short isPresent; int value; };
static inline struct optional_any *_unit(size_t os, size_t vs, const char *v) {
struct optional_any *o = malloc(os);
o->isPresent = 1;
memcpy(&(o->value), v, vs);
return o;
}
#define OPTIONAL_UNIT(t, v) ((OPTIONAL(t) *)_unit(sizeof(OPTIONAL(t)), sizeof(t), (const char *)&v))
#define OPTIONAL_EMPTY(t) ((OPTIONAL(t) *)&_optional_empty)
#define OPTIONAL_BIND(t, o, f) (o->isPresent ? f(o->value) : OPTIONAL_EMPTY(t))
/* Declare an optional int type */
OPTIONAL_INIT(int);
OPTIONAL(int) *maybe_double(int arg) {
int val = arg * 2;
return OPTIONAL_UNIT(int, val);
}
OPTIONAL(int) *maybe_plus_two(int arg) {
int val = arg + 2;
return OPTIONAL_UNIT(int, val);
}
typedef OPTIONAL(int) * (*fn)(int);
/* composition in C: messy. */
static fn f = maybe_double;
static fn g = maybe_plus_two;
OPTIONAL(int) *f_bind_g(int v) {
return OPTIONAL_BIND(int, f(v), g);
}
/* Helper for right identitiy law */
OPTIONAL(int) *unit(int v) {
return OPTIONAL_UNIT(int, v);
}
int main(int argc, char **argv) {
int myval = 5;
OPTIONAL(int) *optint = OPTIONAL_UNIT(int, myval);
// left identity: bind(unit(v), f) === f(v)
OPTIONAL(int) *left1 = OPTIONAL_BIND(int, optint, maybe_double);
OPTIONAL(int) *left2 = maybe_double(myval);
assert(left1->value == left2->value);
// right identity: bind(m, unit) === m
OPTIONAL(int) *right = OPTIONAL_BIND(int, optint, unit);
assert(right->value == optint->value);
// associativity
OPTIONAL(int) *assoc1 = OPTIONAL_BIND(int, OPTIONAL_BIND(int, optint, maybe_double), maybe_plus_two);
OPTIONAL(int) *assoc2 = OPTIONAL_BIND(int, optint, f_bind_g);
assert(assoc1->value == assoc2->value);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment