I'm building a call by name Haskell interpreter and I want to implement a function short :: Val -> Exp -> Error Val
that will evaluate a value applied to an expression. I don't want to evaluate the second arguments of the short-circuited boolean binary operators (i.e. ((&&) False)
and ((||) True))
, nor the argument passed to a unit lambda (\() -> ...
) abstraction.
In a lazy language such as Haskell, I know you get short circuited boolean binary operators built into the Prelude. I was thinking that its possible to do something like this:
short :: Val -> Exp -> Error Val
short (Partial (&&) False) = False
-- Making use of Partial Oper Val that is defined in Val
But I already know that a type error will arise. I'm not sure what to exactly do. Any suggestions?
Some additional definitions:
data Val = VUnit | VNil
| VN Int | VB Bool | VOp Oper
| Partial Oper Val
| VLamUnit Exp (Env Val)
| VLam String Exp (Env Val)
| VListBool [Bool]
| VListInt [Int]
data Exp = Unit | Nil
| N Int | B Bool | Var String | Op Oper
| App Exp Exp
| LamUnit Exp
| Lam String Exp
| If Exp Exp Exp
| Let [(String, Exp)] Exp
data Error a = S a
| Error String
The evaluation of the arguments of a function is first determined by the patterns used to bind the arguments.
If you have
short :: Val -> Exp -> Error Val
short val ex
| condition on val only = something
| other condition on val only = somethingElse
| condition involving ex too = thirdPossibility
| otherwise = whatever
no evaluation takes place on binding the arguments, since the patterns are irrefutable variable patterns, val
is - partially or maybe completely - evaluated to determine the outcome of the first guard. If that evaluates to True
, exp
is only evaluated if it is used in something
and its evaluation is necessary to evaluate something
. Only if the first two conditions evaluate to False
, ex
is evaluated to determine the value of the third guard (and maybe not even then).
If you can shortcut based on the constructor of val
, you can use a refutable pattern for val
and let ex
be a variable pattern (just an identifier), you get the same short-circuiting as for (&&)
etc.
short (Val foo) ex = Okay (foo bar ex)
short (Lav oof) _ = Okay oof
short val ex
| isGood val = oomph
| isBad ex = error "Didn't expect that"
| otherwise = undefined
If you don't use the Exp
argument on the right hand side, you don't even need to give it a name, the wildcard _
says "here is an argument that shall be ignored".
For your desired behaviour of
short :: Val -> Exp -> Error Val
short (Partial (&&) False) = False
that as such doesn't work because False
is a Bool
and not a Val
, you can use
short (Partial OR (VB False)) _ = S False
with a hypothetical constructor OR
of Oper
. (You can use (&&)
there, but that is a variable pattern matching anything.) That pattern doesn't even bind the Exp
argument, so unless its evaluation has been forced by previous equations for short
, it remains unevaluated.