Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upSimplify Dict code, and eliminate calls to Debug.crash #255
Conversation
jvoigtlaender
added some commits
May 27, 2015
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
evancz
May 27, 2015
Member
This looks good to me!
When you say "potentially crashing" do you mean "given arbitrary inputs, it can crash" or do you mean "as it is used in this library, it can crash"?
If the intent is that it should never get to that case because of the invariants of a red-black tree (which are not captured in the types), is it best to have a silent failure/bug or to immediately say something to the user if we have broken those invariants?
|
This looks good to me! When you say "potentially crashing" do you mean "given arbitrary inputs, it can crash" or do you mean "as it is used in this library, it can crash"? If the intent is that it should never get to that case because of the invariants of a red-black tree (which are not captured in the types), is it best to have a silent failure/bug or to immediately say something to the user if we have broken those invariants? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvoigtlaender
May 27, 2015
Contributor
I meant that the function contained a call to crash, so with arbitrary inputs it could crash. But it is called only inside this library, and for the calls in the library an invariant holds that the crashing case cannot occur.
As long as the function remains internal, I think the current use with withDefault at the use site of max in the rem function is the best way to go. If the function were to become used more widespread, then probably changing its return type from Maybe to Result would be best. Then the Err case of the result could carry the current error message string. Even in such a setting, using the Result type would certainly be better than a call to crash?
(Maybe the best idea is to turn that Maybe into Result right away, and add a call to Result.toMaybe at the use site in my refactoring.)
|
I meant that the function contained a call to As long as the function remains internal, I think the current use with (Maybe the best idea is to turn that |
jvoigtlaender
added some commits
May 27, 2015
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvoigtlaender
Aug 14, 2015
Contributor
I merged the master branch into this PR to make sure it still builds. It does, and is still an improvement IMO.
|
I merged the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvoigtlaender
Aug 15, 2015
Contributor
Just a walkthrough of the changes in this pull request (since the comments above do not anymore reflect exactly what happened here overall):
- The
minfunction was deleted. It wasn't used in the module, nor exported. - The
maxfunction was changed from having typeDict k v -> (k, v)and throwing aDebug.crashin one case to having typeResult String (k, v). The function is only used inside the module at present, and its use there is unaffected by this "change" (see below). If the function ever gets exported from the module, signalling error throughResult Stringis certainly better than just crashing. (The consumer of the result might still decide to crash on receiving theErrvalue, of course.) - In the
remfunction, there was a pattern matching on(l, r)vs.(RBNode cl kl vl ll rl, RBNode cr kr vr lr rr)and later bindings oflandrto exactlyRBNode cl kl vl ll rlandRBNode cr kr vr lr rr. Those bindings were clearly superfluous, one can as well pass the originallandron to where they are further needed. So the bindings were deleted. - What remains was a call of
maxon thelfrom the previous bullet point. Since thatlis known to beRBNode cl kl vl ll rlfrom the pattern match, this was actually a callmax (RBNode cl kl vl ll rl). This, I replaced bywithDefault (kl, vl) (toMaybe (max rl)). This is justified by the definition ofmax, which has the cases:
max (RBNode _ key value _ (RBEmpty _)) = Ok (key, value) -- previously, simply (key, value)
max (RBNode _ _ _ _ right) = max right
max (RBEmpty _) = Err "..." -- previously, Debug.crash "..."That clearly makes the above a semantics-preserving transformation. (Because max (RBNode cl kl vl ll rl) will first look at whether rl is an RBEmpty, in which case (kl,vl) are to be returned, just as will happen in the new call withDefault (kl, vl) (toMaybe (max rl)) in that case. If rl is not an RBEmpty, then both the original call and the new call will return what max rl returns -- which is known to never hit an RBEmpty if rl itself was not an RBEmpty.)
(Note: Just for the explanation above, I have abbreviated all RBEmpty_elm_builtin and RBNode_elm_builtin to RBEmpty and RBNode.)
|
Just a walkthrough of the changes in this pull request (since the comments above do not anymore reflect exactly what happened here overall):
max (RBNode _ key value _ (RBEmpty _)) = Ok (key, value) -- previously, simply (key, value)
max (RBNode _ _ _ _ right) = max right
max (RBEmpty _) = Err "..." -- previously, Debug.crash "..."That clearly makes the above a semantics-preserving transformation. (Because (Note: Just for the explanation above, I have abbreviated all |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
evancz
Aug 15, 2015
Member
Do you have any idea what this costs in performance? Perhaps it's not smart to be worrying about this right now, but I have a sense that we will be wanting to make Dict and Array as fast as possible, and it will sometimes be worth it to do weird stuff within the module itself. I guess my concern here is that we are maybe paying quite a lot for this change but not getting a ton back.
I need to get https://github.com/JoeyEremondi/elm-benchmark set up for Joey's ongoing compiler work, so maybe it'd be a good time to start setting up some benchmarks for data structures like this. I'd be curious to see how much we gain by removing a ton of closures from the compiled code. So my vote would be to do some exploration in this direction before going with something that could be quite a bit slower.
|
Do you have any idea what this costs in performance? Perhaps it's not smart to be worrying about this right now, but I have a sense that we will be wanting to make I need to get https://github.com/JoeyEremondi/elm-benchmark set up for Joey's ongoing compiler work, so maybe it'd be a good time to start setting up some benchmarks for data structures like this. I'd be curious to see how much we gain by removing a ton of closures from the compiled code. So my vote would be to do some exploration in this direction before going with something that could be quite a bit slower. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvoigtlaender
Aug 15, 2015
Contributor
Well, part of my change actually removes work: the superfluous reconstruction of trees l and r in those bindings I removed.
Other than that, you are concerned about the Ok constructor? I have no idea whether/what impact that may have on performance.
|
Well, part of my change actually removes work: the superfluous reconstruction of trees Other than that, you are concerned about the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvoigtlaender
Aug 15, 2015
Contributor
Closing this. May make a smaller pull request with just the obvious improvement (and leaving the elimination of min to dead code detection).
|
Closing this. May make a smaller pull request with just the obvious improvement (and leaving the elimination of |
jvoigtlaender commentedMay 27, 2015
MaybeReplaces https://github.com/elm-lang/core/pull/254.