Skip to content

Instantly share code, notes, and snippets.

@ninenine
Last active February 9, 2024 11:17
Show Gist options
  • Save ninenine/558751ffc0aa4df645a316732039a50f to your computer and use it in GitHub Desktop.
Save ninenine/558751ffc0aa4df645a316732039a50f to your computer and use it in GitHub Desktop.
A simple monad-like structure, focusing on the essence of monads.

Maybe Monad Flow in C

This Mermaid diagram illustrates the flow of a simple "Maybe" monad implementation in C.

flowchart TB
    start([Start]) --> createJust{Is it Just?}
    createJust -- Yes --> justValue[("Just (Value 🍰)")]
    createJust -- No --> nothingValue[("Nothing (😞)")]

    justValue --> bindJust{Bind on Just}
    nothingValue --> bindNothing{Bind on Nothing}

    bindJust -- Has Value --> funcApply[Apply Function]
    bindNothing -.-> nothingDirect[Pass Nothing Along]

    funcApply --> resultJust{Result Just?}
    resultJust -- Yes --> justResultValue[("Just (Result 🎁)")]
    resultJust -- No --> justResultNothing[("Nothing (😒)")]

    justResultValue --> print[Print Maybe Int]
    justResultNothing --> print
    nothingDirect -.-> print

    classDef startFill fill:#f9f,stroke:#333,stroke-width:2px;
    classDef justFill fill:#bff7bf,stroke:#333,stroke-width:2px;
    classDef nothingFill fill:#f7bfbf,stroke:#333,stroke-width:2px;
    classDef funcFill fill:#f7f3bf,stroke:#333,stroke-width:2px;
    classDef default fill:#fff,stroke:#333,stroke-width:1px;

    class start startFill;
    class justValue,justResultValue justFill;
    class nothingValue,justResultNothing,nothingDirect nothingFill;
    class funcApply funcFill;
Loading
#include <stdio.h>
#include <stdlib.h>
// Define our monadic hero: The Maybe monad
typedef struct Maybe {
void *value;
char hasValue;
} Maybe;
// Constructor for a Maybe that has a value. Like a party with cake 🍰.
Maybe Just(void *value) {
return (Maybe){ .value = value, .hasValue = 1 };
}
// Constructor for a Maybe that has no value. Like a party without cake 😞.
Maybe Nothing() {
return (Maybe){ .value = NULL, .hasValue = 0 };
}
// Bind operation: Chains operations on Maybe values, because life's a chain of events, right?
Maybe bind(Maybe m, Maybe (*func)(void *)) {
// If our Maybe is a sad Nothing, pass it along without doing anything. Like forwarding spam mail.
if (!m.hasValue) return Nothing();
// Otherwise, unwrap the value, apply the function, and rewrap it. Like a regifted present.
return func(m.value);
}
// Example function to work with our Maybe monad
Maybe square(void *input) {
if (input == NULL) {
// Handling NULL like social awkwardness at a party
return Nothing();
}
int *value = (int *)input;
int *result = malloc(sizeof(int));
*result = (*value) * (*value);
// Wrap our squared result like it's a surprise 🎁
return Just(result);
}
// Print function to demonstrate the Maybe in action
void printMaybeInt(Maybe m) {
if (m.hasValue) {
printf("Just %d πŸŽ‰\n", *(int *)m.value);
} else {
printf("Nothing 😒\n");
}
}
int main() {
int x = 10;
Maybe mx = Just(&x);
// Let's square x, monadically speaking
Maybe squared = bind(mx, square);
printMaybeInt(squared); // Expect "Just 100 πŸŽ‰"
// Now let's try squaring Nothing, just for the existential crisis
Maybe nothing = Nothing();
Maybe squaredNothing = bind(nothing, square);
printMaybeInt(squaredNothing); // Expect "Nothing 😒"
// Clean up our dynamically allocated memory, because memory leaks are like guests who won't leave
if (squared.hasValue) free(squared.value);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment