[bolts] bolts.functioneditor - module for building function mixins?
Jean-Louis Leroy
jl at leroy.nyc
Tue Apr 7 16:58:12 UTC 2020
On Tuesday, 7 April 2020 at 11:50:10 UTC, aliak wrote:
> Am I correct in assuming that FunctionEditor is a way to
> "stringify" various attributes of a function, in order to
> create string versions of new functions, in order to mixin new
> functions?
Yes. In a first step the function is "meta-objectified",
capturing all the function attributes (well not UDAs yet),
parameter storage classes, and whether it is a member or a static
function, in a template instantiation which has:
- accessors for querying all the attributes from a centralized
place
- "mutators" that create new function meta-objects with altered
attributes
- accessors that return strings suitable to be mixed in to
produce function declarations or definitions (and I am going to
add more for making a function pointer, a delegate, and maybe for
adding template parameters)
> Where being able to mock interfaces is one usecase?
Yes.
> What other usecases would there be?
Any situation that involves wrapping a function from a template,
across module boundaries - keeping in mind what Adam Ruppe
explains here:
https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854 (and that I painfully re-discovered by myself).
For example, my new ducktyping library works with two templates:
InterfaceVariable(Interface), a fat pointer that contains a void*
to an object and a Vtbl* to a table of forwarder functions; and
as(T, IV), that returns an InterfaceVariable and builds the
forwarders and Vtbl for type T.
Thus:
interface Geometry
{
@nogc nothrow void move(real dx, real dy);
}
alias IGeometry = InterfaceValue!Geometry;
class Rectangle
{
@nogc nothrow void move(real dx, real dy) { ... }
}
IGeometry geo = new Rectangle(...).as!IGeometry;
geo.move(dx, dy);
...translates (more or less) to:
struct InterfaceVariable!Geometry
{
struct Vtbl
{
@nogc nothrow void function(void* obj, real a0, real a1)
move0;
}
void* obj;
Vtbl* vtbl;
@nogc nothrow void move(real dx, real dy) { vtbl.move0(obj,
a0, a1); }
}
InterfaceVariable!Geometry as!(Rectangle,
InterfaceVariable!Geometry)(Rectangle r)
{
static @nogc nothrow void move(void* obj, real a0, real a1)
{
(cast(Rectangle) obj).move(a0, a1);
}
static InterfaceVariable(Geometry).Vtbl vtbl = { &move0 };
return InterfaceVariable(Geometry)(cast(void*) r, &vtbl);
}
Note that it is essential to preserve `@nogc nothrow` (and any
parameter storage classes even if the example doesn't show this).
> I looked at the real usage you have, but I don't think I
> understood the dispatcher and discriminator
Given e.g.:
module matrix;
Matrix times(double a, virtual!Matrix b);
@method DiagonalMatrix _times(double a, DiagonalMatrix b) { ...
}
The library generates the following code:
// in module matrix
alias times = Method!(matrix, "times", 0).dispatcher;
alias times = Method!(matrix, "times", 0).discriminator;
// in Method!(matrix, "times", 0):
double dispatcher(double a0, Matrix a1) { return
resolve(a1)(a0, a1); }
Method!(matrix, "times", 0) discriminator(MethodTag, double a0,
Matrix a1);
// also a pointer type:
double function(double, Matrix) Spec;
// and a code string to build a wrapper for a method
specialization
// it will be mixed in a context where 'Spec' is an alias to a
specialization
enum wrapper(Spec) = q{
Matrix wrapper(double a0, Matrix a1) { return Spec(a0,
cast(DiagonalMatrix) a1); }
Of course template Method is not allowed to use names like Matrix
or DiagonalMatrix. Instead everything is channeled via what I
call the "anchor", in this case essentially
`__traits(getOverloads, Module, Name)[Index]`.
> If string interpolation was around would the interface you've
> designed be the same?
Yes. The whole point is to avoid manipulating string until the
very end - when specifying the body. And indeed, at that point,
interpolation will help the *client* code a lot.
> I opened an issue in github as well:
> https://github.com/aliak00/bolts/issues/9 just incase it makes
> more sense to discuss it there.
Let's discuss here for the time being, as others may jump in with
useful comments and suggestions.
From the GH "issue":
> For structs and classes I may have had a prototype that went
> something like:
I haven't given a lot of thought to using this approach beyond
functions. Probably manipulating enums and classes are less of a
pain (no parameter storage classes), and may need much fewer
string mixins.
However, I do like the idea of composing e.g. enums using a
meta-objects, like:
mixin(EnumEditor!"Fruit".addEnumerator!"Grapefruit".etc.etc.asString);
On a side note, I would like to have a very simple construct that
turns an identifier into a string (see
https://forum.dlang.org/post/ayzrsajzcelavxdbwbji@forum.dlang.org).
Finally I am not set on the template and module names.
`FunctionEditor` is a bit errr flat. Transmogrify sounds much
better ;-)
More information about the Digitalmars-d
mailing list