2

Should one expect performance differences between these two emptys, or is it merely a matter of stylistic preference?

foo list = case list of
   []      -> True
   (_ : _) -> False

bar list = case list of
   (_ : _) -> False
   _       -> True
5
  • The -ddump-simpl for the four (including from previous question edits) example functions doesn't seem to exhibit any difference. Their core is identical. I'm not sure if this is generalizable though. Commented Mar 5, 2017 at 18:44
  • 3
    The fact that (:) takes arguments isn't really relevant. list is a value that is constructed by either [] or (:); that's the only thing the underlying code needs to look at. Commented Mar 5, 2017 at 19:50
  • @chepner Yes, but the underscores in (_ : _) aren't omittable. [] is nullary and the : constructor binary. Commented Mar 5, 2017 at 20:13
  • I didn't say they could be omitted. The compiler just doesn't generate any code to see what the arguments might be, because you've said with the _ that their values won't be used. list will be a value of some kind that is tagged with (the equivalent of) either [] or (:), and the compiler just needs to look at the tag. Commented Mar 5, 2017 at 20:15
  • You could pass either function the value undefined : undefined, and each one would still return False without any error. Commented Mar 5, 2017 at 20:19

1 Answer 1

12

In general you should not expect performance to change predictably between trivial fiddling around with patterns like what you're asking about, and can often expect the generated code to be identical.

But the way to actually check is to look at core and or benchmark with criterion. In this case the generated code is the same, and indeed GHC seems to actually combine them:

I compiled the snippet above with

ghc -Wall -O2 -ddump-to-file -ddump-simpl -dsuppress-module-prefixes -dsuppress-uniques -fforce-recomp  YourCode.hs

And we see this core:

foo :: forall t. [t] -> Bool
[GblId,
 Arity=1,
 Caf=NoCafRefs,
 Str=DmdType <S,1*U>,
 Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True,
         Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
         Tmpl= \ (@ t) (list [Occ=Once!] :: [t]) ->
                 case list of _ [Occ=Dead] {
                   [] -> True;
                   : _ [Occ=Dead] _ [Occ=Dead] -> False
                 }}]
foo =
  \ (@ t) (list :: [t]) ->
    case list of _ [Occ=Dead] {
      [] -> True;
      : ds ds1 -> False
    }

-- RHS size: {terms: 1, types: 0, coercions: 0}
bar :: forall t. [t] -> Bool
[GblId,
 Arity=1,
 Caf=NoCafRefs,
 Str=DmdType <S,1*U>,
 Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True,
         Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
         Tmpl= \ (@ t) (list [Occ=Once!] :: [t]) ->
                 case list of _ [Occ=Dead] {
                   [] -> True;
                   : _ [Occ=Dead] _ [Occ=Dead] -> False
                 }}]
bar = foo

I think the Tmpl stuff is the original implementation exposed for inlining in other modules, but I'm not certain.

Sign up to request clarification or add additional context in comments.

3 Comments

In your first sentence, "[do] not expect performance to change predictably" and "expect the generated code to be identical" appear to be at odds: surely "I expect the performance not to change" is a prediction!
the bar = foo line is why I recommended using two different files - because I think I heard brian o'sullivan once saying that the optimizer might do stuff like that, therefore he benchmarks similar functions in different modules/packages.
@DanielWagner what I meant was: I usually find doing superficial changes like the op's results in the same code, but in cases where the generated code is different I find both performance differences and generated code differences were unpredictable looking at the original source (thinking here of things like the ordering of branches in the final generated code; it's not clear to me if and how this relates to the boolean operators used, ordering of case statements, etc). But I would be interested if you disagree!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.