Created
June 3, 2022 20:26
-
-
Save jirutka/12ff25d25d53bcb3132eab13396d2ba6 to your computer and use it in GitHub Desktop.
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
#include <stdbool.h> | |
typedef char* str; | |
#define GENERATE_OPTION_TYPES(Option) \ | |
Option(int) \ | |
Option(str) | |
#define GENERATE_RESULT_TYPES(Result) \ | |
Result(str, int) \ | |
Result(long, int) | |
#define GENERATE_RESULT_OK_TYPES(Ok, x) \ | |
Ok(str, x) \ | |
Ok(long, x) | |
#define GENERATE_RESULT_ERR_TYPES(Err, x) \ | |
Err(x, int) | |
///////////////////////// | |
/** Returns type struct `Option(T)` for the specified "generic" type. | |
* | |
* @param T the value type. | |
*/ | |
#define Option(T) struct Option_ ## T | |
/** Returns name of constructor function for `Option(T)` of variant `Some(T)`. | |
* | |
* @param T the value type. | |
*/ | |
#define OptionSome(T) option_some_ ## T | |
/** Defines struct `Option(T)` and constructor function `OptionSome(T)` for | |
* the specified "generic" type. | |
* | |
* @param T the value type. | |
*/ | |
#define DEFINE_OPTION_TYPE(T) \ | |
Option(T) { \ | |
bool tag; \ | |
T value; \ | |
}; \ | |
static inline Option(T) OptionSome(T)(T value) { \ | |
return (Option(T)) { .tag = true, .value = value }; \ | |
} | |
#ifdef GENERATE_OPTION_TYPES | |
/** Creates struct `Option(T)` in variant `Some(T)` with the given value | |
* of type `T`. | |
* | |
* @param value the value to be wrapped in `Some(T)`. | |
*/ | |
#define Some(value) _Generic((value) \ | |
GENERATE_OPTION_TYPES(OPTION_SOME_GENERIC_ENTRY) \ | |
)(value) | |
#define OPTION_SOME_GENERIC_ENTRY(T) , T: (OptionSome(T)) | |
/** Creates struct `Option(T)` in variant `None`. | |
* | |
* @param T type of `Option(T)`. | |
*/ | |
#define None(T) (Option(T)) { .tag = false } | |
GENERATE_OPTION_TYPES(DEFINE_OPTION_TYPE) | |
#endif | |
#define is_some(option) ((option).tag) | |
#define is_none(option) (!(option).tag) | |
//////////////// | |
/** Returns type struct `Result(T, E)` for the specified "generic" types. | |
* | |
* @param T the value type. | |
* @param E the error type. | |
*/ | |
#define Result(T, E) struct Result_ ## T ## __ ## E | |
/** Returns name of constructor function for struct `Result(T, E)` in variant | |
* `Ok(T)` for the specified "generic" types. | |
* | |
* @param T the value type. | |
* @param E the error type. | |
*/ | |
#define ResultOk(T, E) result_ok_ ## T ## __ ## E | |
/** Returns name of constructor function for struct `Result(T, E)` in variant | |
* `Err(E)` for the specified "generic" types. | |
* | |
* @param T the value type. | |
* @param E the error type. | |
*/ | |
#define ResultErr(T, E) result_err_ ## T ## __ ## E | |
/** Defines struct `Result(T, E)` and constructor functions `ResultOk(T, E)` | |
* and `ResultErr(T, E)` for the specified "generic" types. | |
* | |
* @param T the value type. | |
* @param E the error type. | |
*/ | |
#define DEFINE_RESULT_TYPE(T, E) \ | |
Result(T, E) { \ | |
bool tag; \ | |
union { \ | |
T value; \ | |
E err; \ | |
}; \ | |
}; \ | |
static inline Result(T, E) ResultOk(T, E) (T value) { \ | |
return (Result(T, E)) { .tag = true, .value = value }; \ | |
} \ | |
static inline Result(T, E) ResultErr(T, E) (E err) { \ | |
return (Result(T, E)) { .tag = false, .err = err }; \ | |
} | |
#ifdef GENERATE_RESULT_TYPES | |
/** Creates struct `Result(T, E)` in variant `Ok(T)` with the given value | |
* of "generic" type `T`. | |
* | |
* @param ok_value the value to be wrapped. | |
* @param E the error type. | |
*/ | |
#define Ok(ok_value, E) _Generic((ok_value) \ | |
GENERATE_RESULT_OK_TYPES(RESULT_OK_GENERIC_ENTRY, E) \ | |
)(ok_value) | |
#define RESULT_OK_GENERIC_ENTRY(T, E) , T: (ResultOk(T, E)) | |
/** Creates struct `Result(T, E)` in variant `Err(E)` with the given value | |
* of "generic" type `E`. | |
* | |
* @param T the value type. | |
* @param err_value the error value to be wrapped. | |
*/ | |
#define Err(T, err_value) _Generic((err_value) \ | |
GENERATE_RESULT_ERR_TYPES(RESULT_ERR_GENERIC_ENTRY, T) \ | |
)(err_value) | |
#define RESULT_ERR_GENERIC_ENTRY(T, E) , E: (ResultErr(T, E)) | |
GENERATE_RESULT_TYPES(DEFINE_RESULT_TYPE) | |
#endif | |
#define is_ok(result) ((result).tag) | |
#define is_err(result) (!(result).tag) | |
////// | |
/** A statement expression that returns the contained value or a default. | |
* | |
* Option(T): | |
* @code | |
* unwrap_or(Some(42), 100) # => 42 | |
* unwrap_or(None(int), 100) # => 100 | |
* @endcode | |
* | |
* Result(T, E): | |
* @code | |
* unwrap_or(Ok(42, str), 100) # => 42 | |
* unwrap_or(Err(int, "NaN"), 100) # => 100 | |
* @endcode | |
* | |
* @param cont container object (struct with `.tag` and `.value`). | |
* @param default_value the default value to be returned. | |
*/ | |
#define unwrap_or(cont, default_value) ({ \ | |
__typeof__(cont) _cont_##__LINE__ = (cont); \ | |
_cont_##__LINE__.tag ? _cont_##__LINE__.value : (default_value); \ | |
}) |
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
#include <stdio.h> | |
#include <stdlib.h> | |
#include "maybe.h" | |
#define unless(expr) if (!(expr)) | |
#define between(value, min, max) (({ \ | |
__typeof__(value) _value = (value); \ | |
__typeof__(min) _min = (min); \ | |
__typeof__(max) _max = (max); \ | |
_value >= _min && _value <= _max; \ | |
})) | |
#define unwrap_or_else(option, else_block) ({ \ | |
__typeof__(option) _option_##__LINE__ = (option); \ | |
if (!_option_##__LINE__.tag) else_block; \ | |
_option_##__LINE__.value; \ | |
}) | |
#define unwrap_or_else2(option, else_block) ({ \ | |
__typeof__(option) _option_##__LINE__ = (option); \ | |
if (!_option_##__LINE__.tag) { \ | |
__typeof__(option.err) err = _option_##__LINE__.err; \ | |
else_block; \ | |
} \ | |
_option_##__LINE__.value; \ | |
}) | |
#define unwrap(option) ({ \ | |
__typeof__(option) _option_##__LINE__ = (option); \ | |
if (!_option_##__LINE__.tag) { \ | |
fprintf(stderr, "FATAL: File \"%s\", line %d, in %s\n", __FILE__, __LINE__, __func__); abort(); \ | |
} \ | |
_option_##__LINE__.value; \ | |
}) | |
#define unwrap_err(result) ({ \ | |
__typeof__(result) _result_##__LINE__ = (result); \ | |
if (_result_##__LINE__.tag) { \ | |
fprintf(stderr, "FATAL: File \"%s\", line %d, in %s\n", __FILE__, __LINE__, __func__); abort(); \ | |
} \ | |
_result_##__LINE__.err; \ | |
}) | |
#define result_map(result, func) ({ \ | |
__typeof__(result) _result = (result); \ | |
if (_result.tag) _result.value = func(_result.value); \ | |
_result; \ | |
}) | |
#define result_map_err(result, func) ({ \ | |
__typeof__(result) _result = (result); \ | |
if (!_result.tag) _result.err = func(_result.err); \ | |
_result; \ | |
}) | |
#define unwrap_or_else3(result, var, else_block) ({ \ | |
__typeof__(result) _result = (result); \ | |
var = _result.err; \ | |
_result.tag ? _result.value : (else_block); \ | |
}) | |
Option(int) get_foo(bool a) { | |
printf("get_foo() called\n"); | |
return a ? Some(5) : None(int); | |
} | |
Result(str, int) get_string(bool a) { | |
printf("get_string() called\n"); | |
return a ? Ok("Hello, world!", int) : Err(str, 66); | |
} | |
int foo() { | |
puts("Computing foo"); | |
return 5; | |
} | |
str hello(str value) { | |
printf("hello: %s\n", value); | |
return "foo!"; | |
} | |
int main() { | |
/* | |
unless (i == 0 || i == 1) { | |
printf("Hello!\n"); | |
} | |
*/ | |
if between(foo(), 1, 8) { | |
printf("Hello!\n"); | |
} | |
int x = unwrap_or(get_foo(false), 66); | |
printf("x = %d\n", x); | |
int z = unwrap_or_else(get_foo(true), { | |
return 1; | |
}); | |
printf("z = %d\n", z); | |
int a = unwrap(get_foo(true)); | |
printf("a %d\n", a); | |
Result(str, int) b = get_string(false); | |
int c = unwrap_err(b); | |
printf("c %d\n", c); | |
str d = unwrap_or(get_string(true), "foo!"); | |
printf("d %s\n", d); | |
str e = unwrap_or_else2(get_string(false), { | |
printf("> %d\n", err); | |
}); | |
Result(str, int) f = result_map(get_string(false), hello); | |
printf("f %d\n", f.err); | |
str g = unwrap_or_else3(get_string(true), int err, { | |
printf("?? %i\n", err); | |
goto error; | |
""; | |
}); | |
//Result(str, int) xx = Ok(100, int); | |
//Result(str, int) l = Ok(5, int); | |
/* | |
int c; | |
if unwrap(get_foo(true), c) { | |
printf("Hello, world!\n%d\n", c); | |
} | |
*/ | |
//int y = expect(get_foo(true), "the world is collapsing!"); | |
/* | |
int i = unwrap_or( get_foo(true) ) { | |
printf("fuck!\n"); | |
} | |
*/ | |
ret: | |
return 0; | |
error: | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment