Pergunta

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
Foi útil?

Solução

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top