-
Notifications
You must be signed in to change notification settings - Fork 7
user-defined eagerization #8
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
Conversation
+ rename manage-with-courage -> manage-eager-conditions + add manage-with function
Answering: #2 (comment) Making As different implementations, there seem to be a few strategies already mentioned:
There may be other strategies that make sense, but with these 3, I guess about 99% of the cases will be covered. So one might opt for a |
I am a bit afraid of allowing the eagerization to be mutated by the user of the library. |
Well... it's in the However, if library A also adds the Moreover, making |
Oh, you're talking about a global setting? <- #2 (comment) You certainly misunderstood! I am not in favour of a global setting at all! I am in favour if a setting inside the Where does the global setting idea suddenly comes from? |
When would a user want to override eagerization?
I'm thinking:
1) When pr-str becomes a performance bottleneck in their code.
2) When pr-str fails to eagerize an edge case.
The only need for eagerization is to force lazy code to be executed within
the context of manage, to avoid a scenario where a user doesn't realize his
code is lazy and not really being managed, since it runs after leaving the
manage scope.
So we probably want to offer a faster variant of eagerization which
sacrifices correctness. And a version which sacrificies performance for
correctness.
And then we want to offer one that does not eagerize, if the user wants to
be lazy and knows how to handle it, by managing operations inside the lazy
construct instead.
* manage - Most correct form of eagerization
* manage! - Not safe in STM and non eagerizing form of manage (name could
be different)
* manage-as - Takes an enum of eagerization strategies, can be used to
choose between correct, fast or lazy.
* manage-with - Takes an eagerizing fn.
Is this all really necessary? Like how often will #1 and #2 be a concern
to most users?
If we could find an eagerization strategy that is 100% correct and still
fast, we'd only need manage and manage! as #1 and #2 would be taken care of
with manage.
We failed to find one. But can't we find one that is as correct as pr-str,
yet avoids the overhead of creating strings and coercing values to strings?
Basically I wonder if it wouldn't be better for the library to continually
try to improve eagerization both for correctness and performance of manage.
And in the rare cases a user isn't satisfied, manage! would be good enough
for them to do whatever.
Like for #1, the performance issue. Most of the time bypassing eagerization
will actually result in the most performance gains. So manage! would be my
goto for performance scenarios.
For #2, I'm not even sure it's possible for the user to eagerize what
pr-str fails to eagerize. Can we start listing the actual edge cases and
see?
TL;DR
I think an eagerizing manage and a public non eagerizing manage is all
that's needed. Appart from that, we should just look into ways to improve
the correctness and the performance of the eagerization over time. In the
rare cases a user wants to do anything else, the non eagerizing variant of
manage will allow him to do so. But in most scenarios, I think adding more
manage variants or strategies makes the library more complicated both in
implementation and in ease of use, requiring the user to start
understanding why there's so many variant of manage.
…On Mon, Jul 24, 2017, 13:21 kurtosys ***@***.***> wrote:
Oh, you're talking about a global setting?
You certainly misunderstood! I am not in favour of a global setting at
all! I am in favour if a setting inside the manage-function, which is not
global at all. No global settings... I was at any point referring to my
manage-with and manage-as functions... One passes functions, the other is
multimethod based. Both allow for something like (def manage (partial
manage-with ...)) (as I always intended).
Where does the global setting suddenly comes from?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#8 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAktxDoQCVq_3oB1bnM1gD8ijJ_LBOpkks5sRPzggaJpZM4Og4Bz>
.
|
That's exactly what I'm trying to say (I think).
I don't see a use case for That being said, I certainly agree there shouldn't be more than 3 (or maybe 4?) variants in the library and optimizing them over time:
I suppose these would cover about 99.9% of the cases. So, coming back to your proposition of 2 strategies, well, I agree - I might go for 1 or maybe 2 more :p. The difference is that one uses different functions, the other the same functions with different parameters. |
Ya, I'm not personally seing value in having more then one eagerization strategy. This lib is for conditional restarts, not eagerization. But I'll let the maintainer make that choice, obviously if others see value for them, I'll still be able to use it for my needs. The fact there's no guaranteed mechanism for eagerization in Clojure, I'd almost prefer that only a non eagerizing manage existed. Make it very explicit to users that they must design for laziness when they are using laziness. As I said before, even Clojure's try/catch block does not handle laziness. |
Right, I follow that line of thought... and I fully agree on documenting - I've been stating that already a few times as well. However, if there's only 1 strategy, it shouldn't be It seems better to me to document in which cases eagerization doesn't work (with
Right! Avoid It has been stated that one can fork and start changing the strategy, but please refer to #2 . I don't think that's a good idea either. I'd prefer to have 1 library that fits most uses, not having a bunch of forks all doing the same, but with only 1 or 2 lines of code difference. |
I agree, pr-str is hacky. It relies on hoping that each element at level 0 will have an implementation of print which will loop further down one level. The hacky advantage of pr-str is that some implementation of toString on Java types tend to do that also. Maybe instead of (constantly nil), you could have have:
This would make it so we still bank on pr-str for java types, but just walk everything that's Clojure or primitive. In the future, we could even had more concrete java types to cond and have more optimized walkers for them. |
right... but why not using
|
Ya, that can work too. Basically just a hybrid approach, so it uses postwalk when it can, and falls back to pr-str when it can't. So we get speed and we don't regress on coverage. I was thinking, if you look at the postwalk source, that we can do the same, just where they have :else, we would habe it call pr-str, unless it was a primitive from java.lang. |
Something like that. |
I implemented what I was suggesting: #9 |
see #9 :) |
implement both multimethods and fn-passing to provide a user-defined eagerization