Is it possible to get all contexts of a Traversable lazily?

后端 未结 4 1787
一向
一向 2021-02-07 02:56

lens offers holesOf, which is a somewhat more general and powerful version of this hypothetical function:

holesList :: Traversable t
          =>         


        
4条回答
  •  别跟我提以往
    2021-02-07 03:43

    I have not managed to find a really beautiful way to do this. That might be because I'm not clever enough, but I suspect it is an inherent limitation of the type of traverse. But I have found a way that's only a little bit ugly! The key indeed seems to be the extra type argument that Magma uses, which gives us the freedom to build a framework expecting a certain element type and then fill in the elements later.

    data Mag a b t where
      Pure :: t -> Mag a b t
      Map :: (x -> t) -> Mag a b x -> Mag a b t
      Ap :: Mag a b (t -> u) -> Mag a b t -> Mag a b u
      One :: a -> Mag a b b
    
    instance Functor (Mag a b) where
      fmap = Map
    
    instance Applicative (Mag a b) where
      pure = Pure
      (<*>) = Ap
    
    -- We only ever call this with id, so the extra generality
    -- may be silly.
    runMag :: forall a b t. (a -> b) -> Mag a b t -> t
    runMag f = go
      where
        go :: forall u. Mag a b u -> u
        go (Pure t) = t
        go (One a) = f a
        go (Map f x) = f (go x)
        go (Ap fs xs) = go fs (go xs)
    

    We recursively descend a value of type Mag x (a, a -> t a) (t (a, a -> t a)) in parallel with one of type Mag a a (t a) using the latter to produce the a and a -> t a values and the former as a framework for building t (a, a -> t) from those values. x will actually be a; it's left polymorphic to make the "type tetris" a little less confusing.

    -- Precondition: the arguments should actually be the same;
    -- only their types will differ. This justifies the impossibility
    -- of non-matching constructors.
    smash :: forall a x t u.
             Mag x (a, a -> t) u
          -> Mag a a t
          -> u
    smash = go id
      where
        go :: forall r b.
              (r -> t)
           -> Mag x (a, a -> t) b
           -> Mag a a r
           -> b
        go f (Pure x) _ = x
        go f (One x) (One y) = (y, f)
        go f (Map g x) (Map h y) = g (go (f . h) x y)
        go f (Ap fs xs) (Ap gs ys) =
          (go (f . ($ runMag id ys)) fs gs)
          (go (f . runMag id gs) xs ys)
        go _ _ _ = error "Impossible!"
    

    We actually produce both Mag values (of different types!) using a single call to traverse. These two values will actually be represented by a single structure in memory.

    holes :: forall t a. Traversable t => t a -> t (a, a -> t a)
    holes t = smash mag mag
      where
        mag :: Mag a b (t b)
        mag = traverse One t
    

    Now we can play with fun values like

    holes (Reverse [1..])
    

    where Reverse is from Data.Functor.Reverse.

提交回复
热议问题