Closed rcj-siteimprove closed 1 month ago
Latest commit: 4e36f7b47bafd5f66d30d43bc43b994abc30cc34
The changes in this PR will be included in the next version bump.
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
- It looks like
Name.fromDescendants
could be cached, which would solve the 2F/2H duplication.
Right, I'll do that in stead đđź
- Would it make sense to properly define the monad that
fromSteps
is trying to implement? We'd have something like a glorifiedOption
(to handle the "first non-empty otherwise first empty" logic) and could just do a.orElse
on it. OTOH, the full point about looking back from the start makes this weird đ¤
Might be interesting to look into, but I think for another PR.
- The caching in
fromSteps
might be easier to do or understand by storing the results in an array. SincecollectFirst
's mapper accepts the index, it's easy to store the value during the first pass and retrieve it during the second.
I'll look into this.
- It may also make sense to extract the individual steps (2B, âŚ) into separate functions (not sure about how much is locally shared), so that the
fromSteps
call infromElement
is more streamlined (and the full function smaller) (it would also allow testing of individual steps if needed).
I thought about that, and tried somewhat to do it, but it got a little messy. I got a little confused about what a None
actually means when returned from the subfunctions. Does it mean that the function was "applicable" and returned a (possibly empty) name or if they were not applicable and the next step should be executed (it somewhat seems like a mix đ). It looks like, at least in some cases, that we would need to have the function return an Option<Option<T>>
(or Option<T> | null
đ) where the outer option would signal if the step was applicable and the inner would signal if the name was empty or not. It get's even more confusing, in my opinion, since there are also actual empty names i.e. instances of Name
with an empty value (with or without spacing). In my opinion, the whole computation could use a little TLC (it seems to be a breeding ground for bugs anyway đ
).
In this PR, I think it's best to focus on fixing the inefficiency, and then we can follow up with other improvements.
This PR fixes two optimization problems in the call hierarchy of
Name.fromDescendants
:Name.fromSteps
would potentially call the same functions two times unnecessarily due to an.orElse
. This was very bad because the collection of functions passed in would contain up to two functions which would callfromDescendants
recursively making the traversal grow extremely fast for some certain inputs.Name.fromElement
would potentially recurse intoName.fromDescendants
two times with identical argumentsIn the process of troubleshooting, I in-lined and thus eliminated theName.fromSteps
call inName.fromElement
. Despite makingfromElement
more complex, I decided to keep this in-lined because I think passing functions that is part of a recursive loop as arguments into another function which then just invokes them anyway, makes the flow very hard to follow and makes these kinds inefficiencies hard to spot.HoweverName.fromSteps
is called from other places, so it has nonetheless been optimized with a cache to avoid unnecessary re-computation. We could consider in-lining this the few other places it's used using the same approach and eliminate it completely.I have taken care not to change functionality and the test suite didn't detect any regressions, but we might still want to have this regression tested.