Zemyla

Confusing Lens type

Oct 16th, 2025 (edited)
2,224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {-
  2. Actually, now that I look at it, confusing isn't actually that confusing. The main type involved is Curried (Yoneda f) (Yoneda f), which is basically forall r. Yoneda f (a -> r) -> Yoneda f r. This is basically a kind of difference list for Applicatives. And if you flatten the newtype noise, you get:
  3. -}
  4.  
  5. -- Isomorphic to Curried (Yoneda f) (Yoneda f) a
  6. newtype CurrYo f a = CurrYo {
  7.     runCurrYo :: forall r s. (forall t. ((a -> r) -> t) -> f t) -> (r -> s) -> f s
  8.   }
  9.  
  10. instance Functor (CurrYo f) where
  11.   -- fmap f cu returns a CurrYo that fmaps (. f) onto the Yoneda-encoded
  12.   -- argument before passing it to cu.
  13.   fmap f (CurrYo cu) = CurrYo $ \y -> cu $ \c -> y $ \x -> c $ x . f
  14.   {-# INLINE fmap #-}
  15.  
  16. instance Applicative (CurrYo f) where
  17.   -- pure a fmaps ($ a) onto its Yoneda argument.
  18.   pure a = CurrYo $ \y c -> y $ \x -> c $ x a
  19.   {-# INLINE pure #-}
  20.  
  21.   -- cf <*> ca fmaps (.) onto its Yoneda argument before passing
  22.   -- it to cf, then passing the whole thing to ca. This ensures
  23.   -- left-association.
  24.   CurrYo cf <*> CurrYo ca = CurrYo $ \y -> ca $ cf $ \c -> y $ \x -> c $ \f -> x . f
  25.   {-# INLINE (<*>) #-}
  26.  
  27. -- liftCurrYo fa produces a CurrYo that <*>s fa onto the end of the
  28. -- Yoneda-encoded Applicative.
  29. liftCurrYo :: Applicative f => f a -> CurrYo f a
  30. liftCurrYo fa = CurrYo $ \y c -> y (c .) <*> fa
  31. {-# INLINE liftCurrYo #-}
  32.  
  33. -- lowerCurrYo cu first lowers cu to a Yoneda f by passing it pure id,
  34. -- then lowers the Yoneda f to an f by passing it id.
  35. lowerCurrYo :: Applicative f => CurrYo f a -> f a
  36. lowerCurrYo (CurrYo cu) = cu (\c -> pure (c id)) id
  37. {-# INLINE lowerCurrYo #-}
  38.  
  39. {-
  40. -- And with all this, confusing is simple. Lift the function from a -> f b to
  41. -- a -> CurrYo f b, traverse (using the CurrYo Applicative instance,
  42. -- which left-associates), and then lower it to an f t.
  43. confusing :: Applicative f => LensLike (CurrYo f) s t a b -> LensLike f s t a b
  44. confusing t = \f -> lowerCurrYo . t (liftCurrYo . f)
  45.  
  46. -- This also suggests an alternate strategy for traverseByOf - since the
  47. -- CurrYo Applicative instance doesn't refer to the Applicative instance of the
  48. -- base f, using different lift and lower functions allows you to traverse using
  49. -- custom pure and (<*>) functions without using ReifiedApplicative.
  50. traverseByOf ::
  51.   Traversal s t a b ->
  52.   (forall x. x -> f x) ->
  53.   (forall x y. f (x -> y) -> f x -> f y) ->
  54.   (a -> f b) -> s -> f t
  55. traverseByOf t pur app = \f s -> runCurrYo (t (\a -> CurrYo $ \y c -> y (c .) `app` f a) s) (\c -> pur $ c id) id
  56.  
  57. -- And similarly for sequenceByOf.
  58. sequenceByOf ::
  59.   Traversal s t (f b) b ->
  60.   (forall x. x -> f x) ->
  61.   (forall x y. f (x -> y) -> f x -> f y) ->
  62.   s -> f t
  63. sequenceByOf t pur app = \s -> runCurrYo (t (\a -> CurrYo $ \y c -> y (c .) `app` a) s) (\c -> pur $ c id) id
  64. -}
  65.  
  66. {-
  67. EDIT: 2 years later, I realize that you can remove some of the function overhead being carried around by the Functor and Applicative instances, because it doesn't really use the second function of type r -> s. It's not a Curried (Yoneda f) (Yoneda f) anymore; it's just a Curried (Yoneda f) f, so you can't use the equivalent of the standard intance, but a custom instance isn't that hard to write. Adding this for posterity:
  68. -}
  69.  
  70. -- The new Applicative used internally by confusing.
  71. newtype Confuse f a = Confuse { runConfuse :: forall r. (forall t. ((a -> r) -> t) -> f t) -> f r }
  72.  
  73. -- The Functor and Applicative instances are a lot harder to describe now, but they're correct.
  74. instance Functor (Confuse f) where
  75.   fmap f (Confuse m) = Confuse $ \y -> m $ \u -> y $ \c -> u $ c . f
  76.   {-# INLINE fmap #-}
  77.  
  78. -- You still chain the values by passing them into each other right-to-left, to assure left-association.
  79. instance Applicative (Confuse f) where
  80.   pure a = Confuse $ \y -> y ($ a)
  81.   {-# INLINE pure #-}
  82.  
  83.   liftA2 f (Confuse ma) (Confuse mb) = Confuse $ \y -> mb $ \t -> ma $ \u -> y $ \c -> u $ \a -> t $ c . f a
  84.   {-# INLINE liftA2 #-}
  85.  
  86.   Confuse mf <*> Confuse ma = Confuse $ \y -> ma $ \t -> mf $ \u -> y $ \c -> u $ \f -> t $ c . f
  87.   {-# INLINE (<*>) #-}
  88.  
  89. -- Lift an Applicative into Confuse, by sticking a <*> fa onto the end of the chain.
  90. liftConfuse :: Applicative f => f a -> Confuse f a
  91. liftConfuse fa = Confuse $ \y -> y id <*> fa
  92. {-# INLINE liftConfuse #-}
  93.  
  94. -- Lower a Confuse back into the base Applicative, using pure.
  95. lowerConfuse :: Applicative f => Confuse f a -> f a
  96. lowerConfuse (Confuse m) = m $ \c -> pure (c id)
  97. {-# INLINE lowerConfuse #-}
  98.  
  99. -- And then confusing works much the same way.
  100. confusing :: Applicative f => LensLike (Confuse f) s t a b -> LensLike f s t a b
  101. confusing t = \f -> lowerConfuse . t (liftConfuse . f)
  102. {-# INLINE confusing #-}
  103.  
  104. -- We still get to use Confuse to implement traverseByOf.
  105. traverseByOf ::
  106.   LensLike (Confuse f) s t a b ->
  107.   (forall x. x -> f x) ->
  108.   (forall x y. f (x -> y) -> f x -> f y) ->
  109.   LensLike f s t a b
  110. traverseByOf t pur app = \f s -> runConfuse (t (\a -> Confuse $ \y -> app (y id) (f a)) s) $ \y -> pur $ y id
  111. {-# INLINE traverseByOf #-}
  112.  
  113. -- And sequenceByOf as well.
  114. sequenceByOf ::
  115.   LensLike (Confuse f) s t (f b) b ->
  116.   (forall x. x -> f x) ->
  117.   (forall x y. f (x -> y) -> f x -> f y) ->
  118.   s -> f t
  119. sequenceByOf t pur app = \s -> runConfuse (t (\fa -> Confuse $ \y -> app (y id) fa) s) $ \y -> pur $ y id
  120. {-# INLINE sequenceByOf #-}
  121.  
  122.  
Advertisement
Add Comment
Please, Sign In to add comment