{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LiberalTypeSynonyms #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE UndecidableSuperClasses #-} {-| Module : Combinators Description : Lens-like combinators in terms of Tambara modules. Copyright : (c) Mario Román, 2020 License : GPL-3 Maintainer : mromang08@gmail.com Stability : experimental Portability : POSIX Provides combinators for the library of optics in terms of Tambara modules. -} module Combinators where import Prelude hiding (map) import Data.Function import Data.Either import Control.Monad.Writer hiding (Any) import Data.Functor.Identity import Data.Functor.Compose import Data.Void import Control.Monad import Data.Char import Data.List import Data.Monoid hiding (Any) import Text.Printf import Categories import CategoriesInstances import Tambara -- | Viewing is a profunctor that can be used to implement a 'view' -- operation. Viewing is a Tambara module for all the optics that -- admit the 'view' operator. newtype Viewing a b s t = Viewing { getView :: s -> a } instance Profunctor Any (->) Any (->) (Viewing a b) where dimap l _ (Viewing f) = Viewing (f . l) instance Tambara Any (->) Any (->) Any (->) (,) () (,) (,) (Viewing a b) where tambara (Viewing f) = Viewing (f . snd) instance (Monad m) => Tambara Any (->) Any (->) (Algebra m) (->) (,) () (,) (,) (Viewing a b) where tambara = tambara @Any @(->) @Any @(->) @Any @(->) @(,) @() -- | Previewing is a profunctor that can be used to implement a -- 'preview' operation. Previewing is a Tambara module for all the -- optics that admit the 'preview' operator. newtype Previewing a b s t = Previewing { getPreview :: s -> Maybe a } instance Profunctor Any (->) Any (->) (Previewing a b) where dimap l _ (Previewing f) = Previewing (f . l) instance Tambara Any (->) Any (->) Any (->) (,) () (,) (,) (Previewing a b) where tambara (Previewing f) = Previewing (f . snd) instance Tambara Any (->) Any (->) Any (->) Either Void Either Either (Previewing a b) where tambara (Previewing f) = Previewing (either (\_ -> Nothing) f) -- | Setting is a Tambara module for all the optics that admit the -- 'set' operator. newtype Setting a b s t = Setting { getSet :: (a -> b) -> (s -> t) } instance Profunctor Any (->) Any (->) (Setting a b) where dimap l r (Setting f) = Setting (\u -> r . f u . l) instance Tambara Any (->) Any (->) Any (->) (,) () (,) (,) (Setting a b) where tambara (Setting f) = Setting (\u (w,x) -> (w , f u x)) instance Tambara Any (->) Any (->) Any (->) Either Void Either Either (Setting a b) where tambara (Setting f) = Setting (\u -> either Left (Right . f u)) -- | Classifying is a Tambara module for all the optics that admit the -- 'classify' operator. newtype Classifying m a b s t = Classifying { getClassify :: (Monad m) => m s -> b -> t } instance (Monad m) => Profunctor Any (->) Any (->) (Classifying m a b) where dimap l r (Classifying f) = Classifying (\u -> r . f (fmap l u)) instance (Monad m) => Tambara Any (->) Any (->) (Algebra m) (->) (,) () (,) (,) (Classifying m a b) where tambara (Classifying f) = Classifying (\w b -> (algebra (fmap fst w) , f (fmap snd w) b)) -- | Aggregating is a Tambara module for the optics that admit an 'aggregate' operator. newtype Aggregating a b s t = Aggregate { getAggregate :: [s] -> ([a] -> b) -> t } instance Profunctor Any (->) Any (->) (Aggregating a b) where dimap l r (Aggregate f) = Aggregate (\v u -> r $ f (fmap l v) u) instance Tambara Any (->) Any (->) (Algebra []) (->) (,) () (,) (,) (Aggregating a b) where tambara (Aggregate u) = Aggregate (\l f -> (algebra (fmap fst l) , u (fmap snd l) f)) instance Tambara Any (->) Any (->) Applicative Nat Compose Identity App App (Aggregating a b) where tambara (Aggregate h) = Aggregate (\u f -> App $ pure (flip h f) <*> sequenceA (fmap getApp u)) -- | Updating is a Tambara module for the optics admitting an 'update' operator. newtype Updating m a b s t = Update { getUpdate :: (Monad m) => b -> s -> m t } instance (Monad m) => Profunctor Any (->) Any (->) (Updating m a b) where dimap l r (Update u) = Update (\b x -> fmap r (u b (l x))) instance (Monad m) => Profunctor Any (->) Any (Kleisli m) (Updating m a b) where dimap l (Kleisli r) (Update u) = Update (\b x -> u b (l x) >>= r) instance (Monad m) => Tambara Any (->) Any (Kleisli m) (Any) (->) (,) () (,) (,) (Updating m a b) where tambara (Update u) = Update (\b (w , x) -> fmap ((,) w) $ u b x) -- | Replacing is a Tambara module for the optics admitting an 'over' operator. newtype Replacing a b s t = Replace { getReplace :: (a -> b) -> (s -> t) } instance Profunctor Any (->) Any (->) (Replacing a b) where dimap l r (Replace u) = Replace (\f -> r . u f . l) instance Tambara Any (->) Any (->) Any (->) (,) () (,) (,) (Replacing a b) where tambara (Replace u) = Replace (fmap . u) instance Tambara Any (->) Any (->) Any (->) Either Void Either Either (Replacing a b) where tambara (Replace u) = Replace (fmap . u) instance Tambara Any (->) Any (->) Functor Nat Compose Identity App App (Replacing a b) where tambara (Replace u) = Replace ((\f -> App . fmap f . getApp) . u) instance Tambara Any (->) Any (->) Applicative Nat Compose Identity App App (Replacing a b) where tambara = tambara @Any @(->) @Any @(->) @Functor @Nat @Compose @Identity @App @App @(Replacing a b) instance Tambara Any (->) Any (->) Traversable Nat Compose Identity App App (Replacing a b) where tambara = tambara @Any @(->) @Any @(->) @Functor @Nat @Compose @Identity @App @App @(Replacing a b) -- | Inspired by the "view" operator of Kmett et al's lens library. The -- fixity and semantics are such that subsequent field accesses can be -- performed with 'Prelude..'. infixl 8 ^. (^.) :: s -> (Viewing a b a b -> Viewing a b s t) -> a (^.) s l = getView (l $ Viewing id) s -- | Inspired by the "preview" operator of Kmett et al's lens library. The -- fixity and semantics are such that subsequent field accesses can be -- performed with 'Prelude..'. infixl 8 ?. (?.) :: s -> (Previewing a b a b -> Previewing a b s t) -> Maybe a (?.) s l = getPreview (l $ Previewing return) s -- | Inspired by the "set" operator of Kmett et al's lens library. The -- fixity and semantics are such that subsequent field accesses can be -- performed with 'Prelude..'. infixl 8 .~ (.~) :: (Setting a b a b -> Setting a b s t) -> b -> s -> t (.~) l b = getSet (l $ Setting id) (\_ -> b) -- | Inspired by the "over" operator of Kmett et al's lens library. The -- fixity and semantics are such that subsequent field accesses can be -- performed with 'Prelude..'. infixl 8 %~ (%~) :: (Replacing a b a b -> Replacing a b s t) -> (a -> b) -> (s -> t) (%~) l f = getReplace (l $ Replace id) $ f -- | A "classify" operator. The fixity and semantics are such that -- subsequent field accesses can be performed with 'Prelude..'. infixl 8 .? (.?) :: (Monad m) => (Classifying m a b a b -> Classifying m a b s t) -> b -> m s -> t (.?) l b ms = getClassify (l $ Classifying (\a b -> b)) ms b -- | An "aggregate" operator. The fixity and semantics are such that -- subsequent field accesses can be performed with 'Prelude..'. infixl 8 >- (>-) :: (Aggregating a b a b -> Aggregating a b s t) -> ([a] -> b) -> [s] -> t (>-) l = flip $ getAggregate (l $ Aggregate $ flip ($)) -- | An "mupdate" operator. It is prepared to be used with do notation. mupdate :: (Monad m) => (Updating m a b a b -> Updating m a b s t) -> b -> s -> m t mupdate f = getUpdate $ f (Update (\b a -> return b))