Haskell is really lazy. Laziness means that the expression is not evaluated if it is not required. However, laziness does not mean that two expressions can be evaluated in any order. The order in which expressions are evaluated in Haskell matters. For example, consider the und function:
und :: Bool -> Bool -> Bool und False y = False und y False = False
Firstly, I would like to note that this function is incomplete. Full function:
und :: Bool -> Bool -> Bool und False y = False und y False = False und y True = True -- you forgot this case
In fact, the und function can be written more succinctly (and more lazily) as follows:
-- if the first argument is False then the result is False -- otherwise the result is the second argument -- note that the second argument is never inspected und :: Bool -> Bool -> Bool und False _ = False und _ x = x
In any case, the template matching syntax in Haskell is just the syntactic sugar for case expressions. For example, your original (incomplete) function will be directed (before alpha equivalence):
und :: Bool -> Bool -> Bool und xy = case x of False -> False True -> case y of False -> False True -> undefined
From here you can see:
- Your function is incomplete because the last case is
undefined . - Your function evaluates the second argument if the first argument is
True , although it is not needed. Remember that case expressions always force evaluations of the checked expression. - Your function first checks
x and then checks y if x evaluates to True . Therefore, there does indeed exist an explicit evaluation procedure. Note that if x evaluates to False , then y never evaluated (proof that und really lazy).
It is because of this und (non_term 1) False ordering that your expression und (non_term 1) False diverges:
und (non_term 1) False = case non_term 1 of False -> False True -> case False of False -> False True -> undefined = case non_term 2 of False -> False True -> case False of False -> False True -> undefined = case non_term 3 of False -> False True -> case False of False -> False True -> undefined . . . .
If you want, you can create a function that has a different evaluation order:
und :: Bool -> Bool -> Bool und xy = case y of False -> False True -> x -- note that x is never inspected
Now the expression und (non_term 1) False evaluates to False . However, the expression und False (non_term 1) is still diverging. So your main question is:
Is there a way to implement und (i.e. and in German) correctly (not only partially, as described above), so both
und (non_term 1) False
and
und False (non_term 1)
return False?
The short answer is no. You always need a specific evaluation order; and depending on the evaluation order, either und (non_term 1) False or und False (non_term 1) will diverge.
Does this mean that Haskell is wrong / wrong? No. Haskell is doing the right thing and just does not give any answer. For a person (who can evaluate both expressions in parallel), it would seem that the result of und (non_term 1) False should be False . However, computers should always have an evaluation order.
So what is the real problem? In my humble opinion, the actual issue is either / or:
Parallel evaluation. Haskell should evaluate the expression in both directions in parallel and choose the one that ends first:
import Data.Unamb (unamb) type Endo a = a -> a bidirectional :: Endo (a -> a -> b) bidirectional = unamb <*> flip und :: Bool -> Bool -> Bool und = bidirectional (&&)
General recursion. In my humble opinion, general recursion is too efficient for most use cases: it allows you to write absurd functions such as non_term x = non_term (x + 1) . Such functions are completely useless. If we do not consider such useless functions as source data, then your original und function is an excellent function to use (just implement the latter case or use && ).
Hope this helps.