First of all, thank you for all your work on algebraic effects, especially libhandler and Koka -- for me, they have always been a strong source of inspiration and examples to follow.
In this issue, I propose using Metalang99 to clean up the API of libmpeff, as well as to improve its implementation.
Problem
Currently, the header file mpeff.h exposes the set of macros MPE_DECLARE_EFFECT(0-2) and MPE_DEFINE_EFFECT(0-7). Owing to the lack of iteration facilities of the C/C++ preprocessor, these are implemented by copy-pasting the same pattern with respect to a number of macro parameters. Needless to say that this approach leads to error-prone and hard-to-maintain code, and moreover, complicates the API: a user needs to specify the number of parameters like this: MPE_DEFINE_EFFECT5(...) and change the name as the number of parameters changes.
A similar problem is manual macro overloading on a number of parameters:
Besides MPE_DECLARE_OP, there are MPE_DECLARE_VOIDOP, MPE_DEFINE_OP, MPE_DEFINE_VOIDOP, MPE_WRAP_FUN, and MPE_WRAP_VOIDFUN.
Solution
We could rewrite these macros using Metalang99, a standard-conforming header-only library that augments the C99/C++11 preprocessor with extra metaprogramming facilities, including handy iteration patterns.
MPE_DECLARE_EFFECT
With the aid of Metalang99, MPE_DECLARE_EFFECT takes the following form:
In user code, just type MPE_DECLARE_OP(reader, ask, long) and it will overload automatically.
To see how it performs in practice, take a look at my branch. Just git clone https://github.com/Hirrolot/metalang99 to the root project directory and execute the usual build procedure.
Advantages
More convenient API.
Less error-prone, more maintainable implementation.
Disadvantages
A third-party dependency (though it is header-only).
-ftrack-macro-expansion=0 (GCC) or -fmacro-backtrace-limit=1 (Clang) should be specified because otherwise, compilation errors will go insane.
Final words
The aforementioned syntax is not a final result but just an example of how can we refine the API. In theory, the syntax could be even more concise. I will be willing to refactor the source code more if needed and can help with anything. Hopefully, this should not be tough, and the semantics should remain completely the same.
Metalang99 is already stable v1.x.y and it is very unlikely that it will significantly change in near future. It has been tested on Datatype99 and Interface99 with successful results.
I believe libmprompt has a bright future, please do not abandon it.
First of all, thank you for all your work on algebraic effects, especially libhandler and Koka -- for me, they have always been a strong source of inspiration and examples to follow.
In this issue, I propose using Metalang99 to clean up the API of libmpeff, as well as to improve its implementation.
Problem
Currently, the header file
mpeff.h
exposes the set of macrosMPE_DECLARE_EFFECT(0-2)
andMPE_DEFINE_EFFECT(0-7)
. Owing to the lack of iteration facilities of the C/C++ preprocessor, these are implemented by copy-pasting the same pattern with respect to a number of macro parameters. Needless to say that this approach leads to error-prone and hard-to-maintain code, and moreover, complicates the API: a user needs to specify the number of parameters like this:MPE_DEFINE_EFFECT5(...)
and change the name as the number of parameters changes.A similar problem is manual macro overloading on a number of parameters:
Besides
MPE_DECLARE_OP
, there areMPE_DECLARE_VOIDOP
,MPE_DEFINE_OP
,MPE_DEFINE_VOIDOP
,MPE_WRAP_FUN
, andMPE_WRAP_VOIDFUN
.Solution
We could rewrite these macros using Metalang99, a standard-conforming header-only library that augments the C99/C++11 preprocessor with extra metaprogramming facilities, including handy iteration patterns.
MPE_DECLARE_EFFECT
With the aid of Metalang99,
MPE_DECLARE_EFFECT
takes the following form:That is it -- now you can specify an arbitrary amount of parameters (to be precise, up to 63) and it will work fine.
MPE_DEFINE_EFFECT
MPE_DEFINE_EFFECT
would be slightly more complex:Show the definition
```c #define MPE_DEFINE_EFFECT(...) \ ML99_IF( \ ML99_VARIADICS_IS_SINGLE(__VA_ARGS__), \ MPE_PRIV_DEFINE_EFFECT_0, \ MPE_PRIV_DEFINE_EFFECT_N) \ (__VA_ARGS__) #define MPE_PRIV_DEFINE_EFFECT_0(effect) const char *MPE_EFFECT(effect)[2] = {#effect, NULL}; #define MPE_PRIV_DEFINE_EFFECT_N(effect, ...) \ const char *MPE_EFFECT(effect)[ML99_VARIADICS_COUNT(__VA_ARGS__) + 2] = { \ #effect, \ ML99_EVAL(MPE_PRIV_opNameForEach(effect, __VA_ARGS__)) NULL}; \ ML99_EVAL(MPE_PRIV_defineEffectForEach(effect, __VA_ARGS__)) /* * #effect "/" #op1, ..., #effect "/" #opN, */ #define MPE_PRIV_opNameForEach(effect, ...) \ ML99_variadicsForEach(ML99_appl(v(MPE_PRIV_opName), v(effect)), v(__VA_ARGS__)) #define MPE_PRIV_opName_IMPL(effect, op) v(#effect "/" #op, ) /* * const struct mpe_optag_s MPE_OPTAG_DEF(effect, op1) = { MPE_EFFECT(effect), 1 }; * ... * const struct mpe_optag_s MPE_OPTAG_DEF(effect, opN) = { MPE_EFFECT(effect), N }; */ #define MPE_PRIV_defineEffectForEach(effect, ...) \ ML99_variadicsForEachI(ML99_appl(v(MPE_PRIV_defineEffect), v(effect)), v(__VA_ARGS__)) #define MPE_PRIV_defineEffect_IMPL(effect, op, i) \ v(const struct mpe_optag_s MPE_OPTAG_DEF(effect, op) = {MPE_EFFECT(effect), i};) #define MPE_PRIV_opName_ARITY 2 #define MPE_PRIV_defineEffect_ARITY 3 ```Macro overloading
MPE_DECLARE_OP
and its friends could be defined as follows:In user code, just type
MPE_DECLARE_OP(reader, ask, long)
and it will overload automatically.To see how it performs in practice, take a look at my branch. Just
git clone https://github.com/Hirrolot/metalang99
to the root project directory and execute the usual build procedure.Advantages
Disadvantages
-ftrack-macro-expansion=0
(GCC) or-fmacro-backtrace-limit=1
(Clang) should be specified because otherwise, compilation errors will go insane.Final words
The aforementioned syntax is not a final result but just an example of how can we refine the API. In theory, the syntax could be even more concise. I will be willing to refactor the source code more if needed and can help with anything. Hopefully, this should not be tough, and the semantics should remain completely the same.
Metalang99 is already stable v1.x.y and it is very unlikely that it will significantly change in near future. It has been tested on Datatype99 and Interface99 with successful results.
I believe libmprompt has a bright future, please do not abandon it.