haskell - Simplifying a GADT with Uniplate -


i'm trying answer this stackoverflow question, using uniplate suggested, the solution i've come far pretty ugly.

this seems common issue, wanted know if there more elegant solution.

basically, we've got gadt resolves either expression int or expression bool (ignoring codataif = if (b true) codataif codataif):

data expression     :: int -> expression int     b :: bool -> expression bool     add :: expression int  -> expression int  -> expression int     mul :: expression int  -> expression int  -> expression int     eq  :: expression int  -> expression int  -> expression bool     , :: expression bool -> expression bool -> expression bool     or  :: expression bool -> expression bool -> expression bool     if  :: expression bool -> expression    -> expression -> expression 

and (in version of problem) want able evaluate expression tree bottom-up using simple operation combine leaves new leaf:

step :: expression -> expression step = \case   add (i x) (i y)   -> $ x + y   mul (i x) (i y)   -> $ x * y   eq (i x) (i y)    -> b $ x == y   , (b x) (b y)   -> b $ x && y   or (b x) (b y)    -> b $ x || y   if (b b) x y      -> if b x else y   z                 -> z 

i had difficulty using dataderiving derive uniplate , biplate instances (which maybe should have been red flag), rolled own uniplate instances expression int, expression bool, , biplate instances (expression a) (expression a), (expression int) (expression bool), , (expression bool) (expression int).

this let me come these bottom-up traversals:

evalint :: expression int -> expression int evalint = transform step  evalintbi :: expression bool -> expression bool evalintbi = transformbi (step :: expression int -> expression int)  evalbool :: expression bool -> expression bool evalbool = transform step  evalboolbi :: expression int -> expression int evalboolbi = transformbi (step :: expression bool -> expression bool) 

but since each of these can 1 transformation (combine int leaves or bool leaves, not either), can't complete simplification, must chained manually:

λ example1 if (eq (i 0) (add (i 0) (i 0))) (i 1) (i 2) λ evalint if (eq (i 0) (i 0)) (i 1) (i 2) λ evalboolbi if (b true) (i 1) (i 2) λ evalint 1 λ example2 if (eq (i 0) (add (i 0) (i 0))) (b true) (b false) λ evalintbi if (eq (i 0) (i 0)) (b true) (b false) λ evalbool b true 

my hackish workaround define uniplate instance either (expression int) (expression bool):

type  wexp = either (expression int) (expression bool)  instance uniplate wexp   uniplate = \case       left (add x y)    -> plate (i2 left add)  |* left x  |* left y       left (mul x y)    -> plate (i2 left mul)  |* left x  |* left y       left (if b x y)   -> plate (bi2 left if)  |* right b |* left x  |* left y       right (eq x y)    -> plate (i2 right eq)  |* left x  |* left y       right (and x y)   -> plate (b2 right and) |* right x |* right y       right (or x y)    -> plate (b2 right or)  |* right x |* right y       right (if b x y)  -> plate (b3 right if)  |* right b |* right x |* right y       e                 -> plate e     i2 side op (left x) (left y) = side (op x y)           i2 _ _ _ _ = error "type mismatch"           b2 side op (right x) (right y) = side (op x y)           b2 _ _ _ _ = error "type mismatch"           bi2 side op (right x) (left y) (left z) = side (op x y z)           bi2 _ _ _ _ _ = error "type mismatch"           b3 side op (right x) (right y) (right z) = side (op x y z)           b3 _ _ _ _ _ = error "type mismatch"  evalwexp :: wexp -> wexp evalwexp = transform (either (left . step) (right . step)) 

now can complete simplification:

λ evalwexp . left $ example1 left (i 1) λ evalwexp . right $ example2 right (b true) 

but amount of error , wrapping/unwrapping had make work makes feel inelegant , wrong me.

is there right way solve problem with uniplate?

there isn't right way solve problem uniplate, there right way solve problem same mechanism. uniplate library doesn't support uniplating data type kind * -> *, can create class accommodate that. here's little minimal uniplate library types of kind * -> *. based on current git version of uniplate has been changed use applicative instead of str.

{-# language rankntypes #-}  import control.applicative import control.monad.identity  class uniplate1 f     uniplate1 :: applicative m => f -> (forall b. f b -> m (f b)) -> m (f a)      descend1 :: (forall b. f b -> f b) -> f -> f     descend1 f x = runidentity $ descendm1 (pure . f) x      descendm1 :: applicative m => (forall b. f b -> m (f b)) -> f -> m (f a)     descendm1 = flip uniplate1  transform1 :: uniplate1 f => (forall b. f b -> f b) -> f -> f transform1 f = f . descend1 (transform1 f) 

now can write uniplate1 instance expression:

instance uniplate1 expression     uniplate1 e p = case e of         add x y -> lifta2 add (p x) (p y)         mul x y -> lifta2 mul (p x) (p y)         eq  x y -> lifta2 eq  (p x) (p y)         , x y -> lifta2 , (p x) (p y)         or  x y -> lifta2 or  (p x) (p y)         if  b x y -> pure if <*> p b <*> p x <*> p y         e -> pure e 

this instance similar emap function wrote in my answer original question, except instance places each item applicative functor. descend1 lifts argument identity , runidentity's result, making desend1 identical emap. transform1 identical postmap previous answer.

now, can define reduce in terms of transform1.

reduce = transform1 step 

this enough run example:

"reduce" if (and (b true) (or (b false) (b true))) (add (i 1) (mul (i 2) (i 3))) (i 0) 7 

Comments

Popular posts from this blog

javascript - Jquery show_hide, what to add in order to make the page scroll to the bottom of the hidden field once button is clicked -

javascript - Highcharts multi-color line -

javascript - Enter key does not work in search box -