Last active
December 5, 2023 12:57
-
-
Save LAK132/0d264549745e8196df1e632d5b518c37 to your computer and use it in GitHub Desktop.
Result types in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __cplusplus | |
# define decltype typeof | |
# include <stdbool.h> | |
#endif | |
#include <assert.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#define TOKEN_CONCAT_EX(x, y) x##y | |
#define TOKEN_CONCAT(x, y) TOKEN_CONCAT_EX(x, y) | |
#define UNIQUIFY(x) TOKEN_CONCAT(x, __LINE__) | |
#define Result_EX(T, E) result_##T##_##E##_t | |
#define Result(T, E) Result_EX(T, E) | |
#define ResultDecl(T, E) \ | |
typedef struct \ | |
{ \ | |
bool _is_ok; \ | |
union \ | |
{ \ | |
T _ok; \ | |
E _err; \ | |
}; \ | |
} Result(T, E) | |
#define Ok(V, T, E) \ | |
((Result(T, E)){ \ | |
._is_ok = true, \ | |
._ok = V, \ | |
}) | |
#define Err(V, T, E) \ | |
((Result(T, E)){ \ | |
._is_ok = false, \ | |
._err = V, \ | |
}) | |
#define is_ok(V) (V._is_ok) | |
#define is_err(V) (!(V._is_ok)) | |
#define if_let_Ok(I, V) \ | |
if (is_ok(V)) \ | |
for (bool UNIQUIFY(once) = true; UNIQUIFY(once);) \ | |
for (decltype(V._ok) I = V._ok; UNIQUIFY(once); UNIQUIFY(once) = false) | |
#define if_let_Err(I, V) \ | |
if (is_err(V)) \ | |
for (bool UNIQUIFY(once) = true; UNIQUIFY(once);) \ | |
for (decltype(V._err) I = V._err; UNIQUIFY(once); UNIQUIFY(once) = false) | |
#define unwrap(V) ((is_ok(V) ? (void)0 : abort()), V._ok) | |
#define unwrap_err(V) ((is_err(V) ? (void)0 : abort()), V._err) | |
#define get_ok(V) (is_ok(V) ? &(V._ok) : nullptr) | |
#define get_err(V) (is_err(V) ? &(V._err) : nullptr) | |
#define map(V, T, E, F) (is_ok(V) ? Ok(F(V._ok), T, E) : Err(V._err, T, E)) | |
#define map_err(V, T, E, F) \ | |
(is_err(V) ? Ok(V._ok, T, E) : Err(F(V._err), T, E)) | |
#define and_then(V, T, E, F) (is_ok(V) ? F(V._ok) : Err(V._err, T, E)) | |
#define or_else(V, T, E, F) (is_ok(V) ? Ok(V._ok, T, E) : F(V._err)) | |
#define flatten(V, T, E) \ | |
(is_ok(V) ? (is_ok(V._ok) ? Ok(V._ok._ok, T, E) : Err(V._ok._err, T, E)) \ | |
: Err(V._err, T, E)) | |
typedef struct | |
{ | |
int val; | |
} thing_t; | |
ResultDecl(int, bool); | |
ResultDecl(thing_t, bool); | |
ResultDecl(Result(thing_t, bool), bool); | |
thing_t make_thing(int v) | |
{ | |
return (thing_t){.val = v}; | |
} | |
Result(thing_t, bool) maybe_make_thing(int v) | |
{ | |
if (v % 200 == 0) | |
return Err(v == 200, thing_t, bool); | |
else | |
return Ok(make_thing(v), thing_t, bool); | |
} | |
Result(int, bool) test(bool v, int i) | |
{ | |
if (v || i == 0) | |
return Err(v, int, bool); | |
else | |
return Ok(i, int, bool); | |
} | |
int main() | |
{ | |
int input1, input2; | |
fscanf(stdin, "%d %d", &input1, &input2); | |
Result(int, bool) result = test(input1 != 20, input2); | |
if_let_Ok(ok, result) { fprintf(stdout, "%d\n", ok); } | |
else if_let_Err(err, result) | |
{ | |
fprintf(stderr, "%s\n", err ? "true" : "false"); | |
} | |
Result(thing_t, bool) thing = map(result, thing_t, bool, make_thing); | |
if_let_Ok(ok, thing) { fprintf(stdout, "we got a thing %d\n", ok.val); } | |
Result(Result(thing_t, bool), bool) maybe_thing = | |
map(result, Result(thing_t, bool), bool, maybe_make_thing); | |
Result(thing_t, bool) flattened = flatten(maybe_thing, thing_t, bool); | |
if_let_Ok(ok, flattened) | |
{ | |
fprintf(stdout, "we got a flattened thing %d\n", ok.val); | |
} | |
Result(thing_t, bool) and_thend = | |
and_then(result, thing_t, bool, maybe_make_thing); | |
if_let_Ok(ok, and_thend) | |
{ | |
fprintf(stdout, "we got an and_then'd thing %d\n", ok.val); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment