-
-
Notifications
You must be signed in to change notification settings - Fork 79
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
fix: (str nil) => "" #264
base: main
Are you sure you want to change the base?
fix: (str nil) => "" #264
Conversation
Thanks for tackling this Bill, could you fix these stylistic issues to help me see the approach without getting distracted? |
Also please add |
@frenchy64 Ugh.. maybe this one test is stuck? Any way to restart it? |
What's stuck? |
@jeaye Nothing now. It was running for 26 or so mins when I sent that. It probably resolved as soon as I did, of course. |
Static analysis is very slow. It's amazing what it finds, though. 🙂 |
if(!is_nil(o)) | ||
{ | ||
runtime::to_string(o, buff); | ||
} | ||
if(0 < sequence_length(typed_args)) |
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.
Question for @jeaye: can we remove this conditional? persistent_list::fresh_seq()
will return nullptr
and short-circuit the for
, then we just need to check the count once.
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.
Yeah, that makes sense. The for loop with fresh_seq
should handle this just fine.
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.
Nice work finding your way when I led you down the wrong path, Bill! 🙂
(if (nil? o) | ||
"" | ||
(clojure.core-native/to-string o))) |
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.
We're already doing the nil
check in the C++ code. The way I read it, this extra nil?
check is not needed. Is that right?
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.
The trick here is that this line is calling to-string
, which does not have the nil
check. I added the nil
check to str
. So, I think this is right.
a03b7cf
to
7508ea3
Compare
for(auto it(fresh->next_in_place()); it != nullptr; it = it->next_in_place()) | ||
runtime::to_string(o, buff); | ||
} | ||
for(auto it(typed_args->fresh_seq()); it != nullptr; it = it->next_in_place()) |
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.
Maybe not urgent, but I think everything above the for
could be pulled out of the visitor to reduce the generated code. Then the buff
could be passed as an argument.
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.
I have no idea if it makes a difference in practice, but I've seen other parts of the code pass in the argument explicitly rather than make a closure.
jank/compiler+runtime/src/cpp/jank/runtime/core.cpp
Lines 27 to 40 in 1a75d85
return visit_object( | |
[](auto const typed_l, auto const r) -> native_integer { | |
using L = typename decltype(typed_l)::value_type; | |
if constexpr(behavior::comparable<L>) | |
{ | |
return typed_l->compare(*r); | |
} | |
else | |
{ | |
throw std::runtime_error{ fmt::format("not comparable: {}", typed_l->to_string()) }; | |
} | |
}, | |
l, | |
r); |
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.
oh, just saw this follow-up comment
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.
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.
There are two approaches here:
- Use a closure (which Bill is currently doing with
[&]
) - Pass the explicit arg
The difference impacts runtime perf. The reason is that the closure generates to a struct when the C++ compiler does its thing. This struct has members for the captured objects. Calling the closure requires creating an instance of that struct, passing in the captured data, and then calling the call operator on that instance. This becomes harder to inline.
On the other hand, if we just use a [](auto const typed_args, util::string_builder &buff)
then we can pass buff
in as an arg. The C++ compiler will generate a free function which can be more easily inlined. We could use auto &buff
, but that means implicitly adding a second type parameter, which then impacts compile-time perf more.
Overall, the difference between the two is marginal and the cases where I've used the second have been due to the profiler saying it'd help. Given that str
is a very common function, I think it's a good bet to default to the faster option, since we're talking about it, but I probably wouldn't have put all of that info on Bill if it hadn't been brought up already.
@frenchy64 You got your wish! |
Closes #241
Here are a some notes on the approach I took and why it differed from what was expected for this ticket.
The original behavior:
First-attempt changes:
Behavior after first attempt
After some time spent trying and failing to correct the above, I eventually went to the Clojure source:
Clojure
str
:https://github.com/clojure/clojure/blob/clojure-1.11.1/src/clj/clojure/core.clj#L546
Taking inspiration from Clojure, I decided to implement this
nil
behavior in jank'sstr
functions. In the results below, all but the first case show correct behavior. The failing case is not a regression.New behavior
Also, this had no effect on:
keyword differs from CLJ/S for non-strings
#246