I'm writing a mini-language, and I have a situation where I'm trying to process an abstract syntax tree such that it either returns an exception, or a function which will resolve to a bool. Here's a minimal example:
data MyException = Can'tResolveToBool
data InputToBeAddedLater =
InputToBeAddedLater String
data Exp a =
ReturnsBoolFunc String (Exp a)
| IsBool Bool
| ReturnsExpFunc String (Exp a)
resolveToExp :: String -> Exp a -> (InputToBeAddedLater -> Exp a)
resolveToExp funcName arg
| funcName == "patently_false" = (\_ -> IsBool False)
resolveToBool :: Exp a -> Either MyException Bool
resolveToBool (ReturnsBoolFunc funcName (ReturnsExpFunc fn arg)) =
let resolved = resolveToExp fn arg
in case resolved of
((InputToBeAddedLater _) -> (IsBool bool)) -> Right bool
((InputToBeAddedLater _) -> (ReturnsExpFunc _ _)) -> Left Can'tResolveToBool
The last line is my (sadly, non-functional) attempt to pattern match on a function type. GHC recommends I try the ViewPatterns
extension, but looking briefly at this, I'm not sure that it's what I need. Is it conceptually impossible to do what I'm trying to do here?
Thanks!
Let's simplify the question dramatically. This is the core of your question: can I implement desired
?
desired :: (String -> Bool) -> Bool
desired f = case f of
(_ -> True ) -> True
(_ -> False) -> False
Hopefully with all of the chaff winnowed away it's clear that there's no way this can be done: there is not necessarily a fact of the matter about whether f
returns True
or False
. The value it returns is allowed to depend on what input you pass it!
What, as I understand it, you try to express with the pattern
case resolved of
((InputToBeAddedLater _) -> (IsBool bool)) -> ...
is to check whether resolved
returns for any input something with the outer constructor IsBool
. I.e. whether it is of the form
resolved (InputToBeAddedLater i) = IsBool (...)
This you can at least validate without needing to provide a concrete i
, thanks to non-strictness:
case resolved (InputToBeAddedLater undefined) of
(IsBool bool) -> Right bool
(ReturnsExpFunc _ _) -> Left Can'tResolveToBool
The problem with this is that it blows up if resolved
actually evaluates its argument before providing an outer constructor, for example
resolved (InputToBeAddedLater "") = IsBool False
resolved (InputToBeAddedLater s) = ReturnsExpFunc "" (IsBool True)
You could now add some error-catching mechanism on top of this, but that can only really be done in IO and is anyways hacky.
Really, I think this is an XY question, and what you should actually do is give resolved
a way to signal its result's outer constructor without having any input at all. This can be achieved by generalising Exp
so it includes the InputToBeAddedLater
, but only after revealing the outermost constructor:
data ExpH m a
= ReturnsBoolFunc (m (String, Exp a))
| IsBool (m Bool)
| ReturnsExpFunc (m (String, Exp a))
type Exp = ExpH Identity
resolveToExp :: String -> Exp a -> ExpH ((->) InputToBeAddedLater) a
resolveToExp funcName arg
| funcName == "patently_false" = IsBool (const False)
resolveToBool :: Exp a -> Either MyException Bool
resolveToBool (ReturnsBoolFunc funcName (Identity (ReturnsExpFunc fn arg)))
= case resolveToExp fn arg of
IsBool b -> Right (b undefined) -- here you would actually provide an argument
ReturnsExpFunc _ -> Left Can'tResolveToBool
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