Im trying to create a model of a propositional logic in Haskell, and i need a function to apply some logic rules to specific subexpressions. The function "apply" takes a list which indicate the position of the subexpression in the tree (in terms of right and left sequences), a logic rule and a logic expression and returns a new logic expression.
data LogicExp a = P a |
True' |
False' |
Not' (LogicExp a) |
(LogicExp a) :& (LogicExp a) |
(LogicExp a) :| (LogicExp a) |
(LogicExp a) :=> (LogicExp a) |
(LogicExp a) := (LogicExp a)
deriving Show
type LExp = LogicExp String
data Position = L | R
deMorgan :: LExp -> LExp
deMorgan (e1 :& e2) = Not' ((Not e1) :| (Not e2))
deMorgan (e1 :| e2) = Not' ((Not e1) :& (Not e2))
deMorgan x = x
apply :: [Position] -> (LExp -> LExp) -> LExp -> LExp
apply [] f e = f e
apply (L:xs) f (e1 :& e2) = (apply xs f e1) :& e2
apply (R:xs) f (e1 :& e2) = e1 :& (apply xs f e2)
apply (L:xs) f (e1 :| e2) = (apply xs f e1) :| e2
apply (R:xs) f (e1 :| e2) = e1 :| (apply xs f e2)
apply (L:xs) f (e1 :=> e2) = (apply xs f e1) :=> e2
apply (R:xs) f (e1 :=> e2) = e1 :=> (apply xs f e2)
apply (L:xs) f (e1 := e2) = (apply xs f e1) := e2
apply (R:xs) f (e1 := e2) = e1 := (apply xs f e2)
apply (x:xs) f (Not' e) = apply xs f e
The function works fine. But can I use some data constructor "wildcard" to have a more simple function like this?
apply :: [Position] -> (LExp -> LExp) -> LExp -> LExp
apply [] f e = f e
apply (L:xs) f (e1 ?? e2) = (apply xs f e1) ?? e2
apply (R:xs) f (e1 ?? e2) = e1 ?? (apply xs f e2)
apply (x:xs) f (Not' e) = apply xs f e
At the moment I can't recall any fancy tricks for doing that. One thing you might want to do, however, is factoring out the common structure in your LogicExp constructors:
data LogicExp a
= P a
| True'
| False'
| Not' (LogicExp a)
| Bin' BinaryOp (LogicExp a) (LogicExp a)
deriving Show
data BinaryOp = And' | Or' | Impl' | Equiv'
deriving Show
apply :: [Position] -> (LExp -> LExp) -> LExp -> LExp
apply [] f e = f e
apply (L:xs) f (Bin' op e1 e2) = Bin' op (apply xs f e1) e2
apply (R:xs) f (Bin' op e1 e2) = Bin' op e1 (apply xs f e2)
apply (x:xs) f (Not' e) = apply xs f e
-- ... and the P, True' and False' cases.
By doing that you lose the cute infix constructors. If you really want them back, however, there is a fancy trick: view patterns (see also this question for more examples and discussion).
This is a classic case for using one of the Generics packages, either syb or uniplate.
Generally uniplate is faster but not as capable as syb. Fortunately in this case you can get away with using uniplate.
Steps to make use of uniplate:
DeriveDataTypeable pragma.Data and Typeable
Data.Data and a uniplate module like Data.Generics.Uniplate.Data
The transformation function you want is simply transform with the appropriate type signature:
doit :: LExp -> LExp
doit = transform deMorgan
where deMorgan is exactly as you have written it.
Complete example:
{-# LANGUAGE DeriveDataTypeable #-}
module Lib6 where
import Data.Data
import Data.Generics.Uniplate.Data
import Text.Show.Pretty (ppShow)
data LogicExp a = P a |
True' |
False' |
Not' (LogicExp a) |
(LogicExp a) :& (LogicExp a) |
(LogicExp a) :| (LogicExp a) |
(LogicExp a) :=> (LogicExp a) |
(LogicExp a) := (LogicExp a)
deriving (Show, Data, Typeable)
type LExp = LogicExp String
data Position = L | R
deMorgan :: LExp -> LExp
deMorgan (e1 :& e2) = Not' ((Not' e1) :| (Not' e2))
deMorgan (e1 :| e2) = Not' ((Not' e1) :& (Not' e2))
deMorgan x = x
doit :: LExp -> LExp
doit = transform deMorgan
example = (P "a" :& P "b") :| (P "c")
test = putStrLn $ ppShow (doit example)
Running test produces:
Not' (Not' (Not' (Not' (P "a") :| Not' (P "b"))) :& Not' (P "c"))
An intro tutorial on uniplate:
http://community.haskell.org/~ndm/darcs/uniplate/uniplate.htm
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