-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Global lambdas #24
base: master
Are you sure you want to change the base?
Global lambdas #24
Conversation
…mmy impl for native context without success
… comment expensive compile time tests
…d + minimal cleanup
Hi, I am requesting a first review because:
ResultsCurrently only global lambdas for native context with unique parameter works (so with const dummy_text = "UPPER TEXT";
const PocLambdas = struct {
pub fn upper1arg(ctx: LambdaContext) !void {
try ctx.write(dummy_text);
}
};
const Dummy = struct { .content: const u8, };
test "interpolation & section: 1 arg" {
const allocator = std.testing.allocator;
const template_interpolation = "{{upper1arg}}";
const template_section = "{{#upper1arg}}poc{{/upper1arg}}"
const dummy = Dummy{ .content = "text", };
const ptr = &dummy;
const result_interpolation = try allocRendertextWithOptions(
allocator,
template_interpolation,
ptr,
.{ .global_lambdas = PocLambdas, },
);
defer allocator.free(result_interpolation);
const result_section = try allocRendertextWithOptions(
allocator,
template_section,
ptr,
.{ .global_lambdas = PocLambdas, },
);
defer allocator.free(result_section);
try std.testing.expectEqualString(dummy_text, result_interpolation);
try std.testing.expectEqualString(dummy_text, result_section);
} But that's it. More advanced features are not yet implemented. The changes I madeMost of them are in the
No more. What next ?The next step will allow global lambdas to have another parameter. The user could make things more interesting like this: const PocLambdas = struct {
pub fn upper(text: *const TextWithAllocator, ctx: LambdaContext) !void {
const upper_text = try std.ascii.allocUpperString(text.allocator, text.content);
defer text.allocator.free(upper_text);
try ctx.write(upper_text);
}
};
const TextWithAllocator = struct {
.content: const u8,
.allocator: std.mem.Allocator,
};
test "interpolation" {
const allocator = std.testing.allocator;
const template = "{{upper}}";
const text = TextWithAllocator{
.content = "upper text",
.allocator = allocator,
};
const ptr_text = &text;
const result = try allocRendertextWithOptions(
allocator,
template,
ptr_text,
.{ .global_lambdas = PocLambdas, },
);
defer allocator.free(result);
try std.testing.expectEqualString("UPPER TEXT", result);
} And here, troubles are coming: when I add a new parameter to the global lambda, the mentioned methods ( It is coming from the In the zig code above, The solution I am thinking about to make This is why, before going further, I am wondering if I choosed the right approach. If not, what can I do instead ? If yes, do you have any tips to go further ? Thank you. |
Hey! Thanks for the first review, I'll start reading through! Just a comment about lambdas with only the const PocLambdas = struct {
pub fn upper(ctx: LambdaContext) !void {
var text = try ctx.renderAlloc(testing.allocator, ctx.inner_text);
defer testing.allocator.free(text);
for (text, 0..) |char, i| {
text[i] = std.ascii.toUpper(char);
}
try ctx.write(text);
}
}
const Dummy = struct { content: []const u8 };
test "interpolation & section: 1 arg" {
const template = "{{#upper}}{{content}}{{/upper}}"
const dummy = Dummy{ .content = "text", };
const result_interpolation = try allocRenderTextWithOptions(
std.testing.allocator,
template,
&dummy,
.{ .global_lambdas = PocLambdas, },
);
defer allocator.free(result_interpolation);
try std.testing.expectEqualString(result_interpolation, "TEXT");
} It just reminded me that I also planned to propagate the allocator in |
Here some news: I found a solution to make |
I was a little bit too enthusiast: Currently, in a native context, only 2-parameters global lambdas are working for interpolation. So this exemple is expected to work: const PocLambdas = struct {
pub fn upper(text: *Text, ctx: LambdaContext) !void {
// Now the user allocator is propagated
text.content = try std.ascii.allocUpperString(ctx.allocator.?, text.content);
defer ctx.allocator.?.free(text.content);
try ctx.write(text.content);
}
}
const Text = struct { content: []const u8 };
test "interpolation: 2 parameters" {
const allocator = std.testing.allocator;
const lower_text = "text";
const upper_text = "TEXT";
const template = "{{upper}}";
var text = Text{
.content = lower_text,
};
const ptr_text = &text;
const result = try mustache.allocRenderTextWithOptions(
allocator,
template,
ptr_text,
.{ .global_lambdas = PocLambdas, },
};
defer allocator.free(result);
try std.testing.expectEqualStrings(upper_text, result);
} but because I am using section instead of interpolation in this one, it does not work: const PocLambdas = struct {
pub fn lower_then_upper(text: *Text, ctx: LambdaContext) !void {
try ctx.write(text.content);
var content = try ctx.renderAlloc(ctx.allocator.?, ctx.inner_text);
defer ctx.allocator.?.free(content);
for (content, 0..) |char, i| {
content[i] = std.ascii.toUpper(char);
}
try ctx.write(" ");
try ctx.write(content);
}
}
const Text = struct { content: []const u8 };
test "section: 2 parameters" {
const allocator = std.testing.allocator;
const lower_text = "text";
const upper_text = "TEXT";
const template = "{{#lower_then_upper}}{{content}}{{/lower_then_upper}";
var text = Text{
.content = lower_text,
};
const ptr_text = &text;
const result = try mustache.allocRenderTextWithOptions(
allocator,
template,
ptr_text,
.{ .global_lambdas = PocLambdas, },
};
defer allocator.free(result);
try std.testing.expectEqualStrings(lower_text ++ " " ++ upper_text, result);
} I will investigate on this issue during the next days. Again, if you have any idea to help me in this task or to improve what I have already done, please do not hesitate to tell me. I really would like to have your feedback about what I am doing. |
Ok get it. Finally it was not a big deal. I expected a more sneaky edge case. Hopefully it was not. So now the PoC is ready for review. Maybe before thinking how we can implement the same logic for JSON we probably should spend some time to clean dirty hacks I used to make the PoC works. To be totally honest I used some shortcuts to make things works but I am not really proud of what I have done. If you think a voice call could make the review process faster, I am not against this eventuality. Thank you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before we start ...
I must say, this was my first project in Zig for which I went beyond the trivial. Besides learning and getting familiar with Zig, one of my goals was to measure how maintainable a Zig codebase could be.
That is, I planned to leave something intentionally unimplemented and get back to it 1+ year later, just for fun, to see if after all this time I would react like "What the hack does it do? 🫤 " or "Actually, it makes a lot of sense 💡".
So, congrats (and thank you) for doing that in my place!
In my opinion, this experiment worked even better with a fresh pair of eyes: I can conclude that Zig code is really great to read (and therefore to maintain). However, YOU deserve a fair share of this! I am impressed by how fast you figured it out, brilliant work!
now let's go ...
I'm halfway through the review, still want to test how we should handle sections for lambdas with 2 arguments.
Please, add your comments about the "shortcuts" you took, and maybe I can help.
src/poc.zig
Outdated
} | ||
}; | ||
|
||
// a bit of color to enlight my tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄
Hi @batiati, here some news: I am currently more confident with what I have done. I fixed most of shortcuts I used and for the remaining ones (#24 (comment) and #24 (comment)), it is "elegance" issues. I prefer going further and eventually coming back on this later. Please let me know if:
I will be happy to read you. |
Hi, I think to close this PR: I worked on this, some months ago, when I had free time. I was stuck when implementing Global Lambdas for JSON context. So I decided to take some weeks to get fresh air, think about this and come back later. So more recently, I came back on this and I realized that I do not really need to implement global lambdas for JSON context. I find another way to achieve what I want to do without this feature. What I made here could be useful if someone wants to go further. I let you decide what to do with. Thank you for your help, your time, your reviews and this awesome project. |
Hello @tiawl!
I'm curious about how you solved it! {{#lambda}}
{{json_value}}
{{/lambda}} The drawback is that the lambda function must operate over the string rendered from the JSON value, limiting the possibilities.
Let's keep it open! |
Currently empty.
Will close #23 when finalized.
As stated in the mentioned issue, I will ask for review when a step is ready (POC, test, doc or implementation)
TODO:
LambdaContext
for the operations that take an allocator