A corrected quiz in my textbook is asking me how many of f
's arguments are strict, f
being:
f x 0 z = x == z
f x y z = x
My initial thought was that all of f
's arguments are to be considered strict, since y
is being evaluated to check if its equal to 0
, and x
and z
are compared to see that they're both equal.
And yet the answer is that only x
and y
are strict.
Any clues as to why?
First of all, you need a very precise definition of "strict" in order for this to make sense. A function f
is strict iff evaluating f x
to whnf causes x
to be evaluated to whnf. The interaction this has with currying is a bit awkward, and I'm going to ignore some of the potential weirdness that introduces.
Assuming the type here is f :: Bool -> Int -> Bool -> Bool
your analysis of the behavior wrt y
is correct - evaluating f x y z
to whnf will always require evaluating y
to determine which equation to choose. As that is the only factor determining which equation to use, we have to split the analysis for x
and z
. In the first equation, evaluating the result to whnf results in both x
and z
being evaluated. In the second equation, evaluating the result to whnf results in evaluating x
to whnf.
Since x
is evaluated in both branches, this function is strict in x
. This is a little bit amusing - it's strict in the way id
is strict. But that's still valid! z
, however, is a different story. Only one of the branches causes z
to be evaluated, so it's not evaluated strictly - it's only evaluated on demand. Usually we talk about this happening where evaluation is guarded behind a constructor or when a function is applied and the result isn't evaluated, but being conditionally evaluated is sufficient. f True 1 undefined
evaluates to True
. If f
was strict in z
, that would have to evaluate to undefined
.
It turns out that whether f
is strict in its second argument depends on what type it gets resolved to.
Here's proof:
data ModOne = Zero
instance Eq ModOne where
_ == _ = True -- after all, they're both Zero, right?
instance Num ModOne -- the method implementations literally don't matter
f x 0 z = x == z
f x y z = x
Now in ghci:
> f True (undefined :: ModOne) True
True
> f True (undefined :: Int) True
*** Exception: Prelude.undefined
And, in a related way, whether f
is strict in its third argument depends on what values you pick for the first two. Proof, again:
> f True 1 undefined
True
> f True 0 undefined
*** Exception: Prelude.undefined
So, there isn't really a simple answer to this question! f
is definitely strict in its first argument; but the other two are conditionally one or the other depending on circumstances.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With