Simplifying a GADT with Uniplate

前端 未结 1 1276
情歌与酒
情歌与酒 2020-12-09 11:25

I\'m trying to answer this stackoverflow question, using uniplate as I suggested, but the only solution I\'ve come up with so far is pretty ugly.

This seems like a f

相关标签:
1条回答
  • 2020-12-09 11:44

    There isn't a right way to solve this problem with uniplate, but there is a right way to solve this problem with the same mechanism. The uniplate library doesn't support uniplating a data type with kind * -> *, but we can create another class to accommodate that. Here's a little minimal uniplate library for types of kind * -> *. It is based on the current git version of Uniplate that has been changed to use Applicative instead of Str.

    {-# LANGUAGE RankNTypes #-}
    
    import Control.Applicative
    import Control.Monad.Identity
    
    class Uniplate1 f where
        uniplate1 :: Applicative m => f a -> (forall b. f b -> m (f b)) -> m (f a)
    
        descend1 :: (forall b. f b -> f b) -> f a -> f a
        descend1 f x = runIdentity $ descendM1 (pure . f) x
    
        descendM1 :: Applicative m => (forall b. f b -> m (f b)) -> f a -> m (f a)
        descendM1 = flip uniplate1
    
    transform1 :: Uniplate1 f => (forall b. f b -> f b) -> f a -> f a
    transform1 f = f . descend1 (transform1 f)
    

    Now we can write a Uniplate1 instance for Expression:

    instance Uniplate1 Expression where
        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)
            And x y -> liftA2 And (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 is very similar to the emap function I wrote in my answer to the original question, except this instance places each item into an Applicative Functor. descend1 simply lifts its argument into Identity and runIdentity's the result, making desend1 identical to emap. Thus transform1 is identical to postmap from the previous answer.

    Now, we can define reduce in terms of transform1.

    reduce = transform1 step
    

    This is enough to run an example:

    "reduce"
    If (And (B True) (Or (B False) (B True))) (Add (I 1) (Mul (I 2) (I 3))) (I 0)
    I 7
    
    0 讨论(0)
提交回复
热议问题