{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
module Frames.Folds
(
EndoFold
, FoldEndo(..)
, FoldFieldEndo(..)
, FoldRecord(..)
, toFoldRecord
, recFieldF
, fieldToFieldFold
, sequenceRecFold
, sequenceEndoFolds
, foldAll
, ConstrainedFoldable
, foldAllConstrained
, foldAllMonoid
, monoidWrapperToFold
, MonoidalField
)
where
import qualified Control.Foldl as FL
import qualified Control.Newtype as N
#if MIN_VERSION_base(4,11,0)
#else
import Data.Monoid ( (<>) )
import Data.Monoid ( Monoid(..))
#endif
import qualified Data.Profunctor as P
import qualified Data.Vinyl as V
import qualified Data.Vinyl.TypeLevel as V
import qualified Data.Vinyl.Functor as V
import qualified Frames as F
import qualified Frames.Melt as F
type EndoFold a = FL.Fold a a
fieldFold
:: (V.KnownField t, a ~ V.Snd t) => EndoFold a -> EndoFold (F.ElField t)
fieldFold = P.dimap (\(V.Field x) -> x) V.Field
{-# INLINABLE fieldFold #-}
newtype FoldEndo t = FoldEndo { unFoldEndo :: EndoFold (V.Snd t) }
newtype FoldFieldEndo f a = FoldFieldEndo { unFoldFieldEndo :: EndoFold (f a) }
newtype FoldRecord f rs a = FoldRecord { unFoldRecord :: FL.Fold (F.Record rs) (f a) }
toFoldRecord
:: V.KnownField t
=> FL.Fold (F.Record rs) (V.Snd t)
-> FoldRecord F.ElField rs t
toFoldRecord = FoldRecord . fmap V.Field
{-# INLINABLE toFoldRecord #-}
recFieldF
:: forall t rs a
. V.KnownField t
=> FL.Fold a (V.Snd t)
-> (F.Record rs -> a)
-> FoldRecord V.ElField rs t
recFieldF fld fromRec = FoldRecord $ P.dimap fromRec V.Field fld
{-# INLINABLE recFieldF #-}
fieldToFieldFold
:: forall x y rs
. (V.KnownField x, V.KnownField y, F.ElemOf rs x)
=> FL.Fold (V.Snd x) (V.Snd y)
-> FoldRecord F.ElField rs y
fieldToFieldFold fld = recFieldF fld (F.rgetField @x)
{-# INLINABLE fieldToFieldFold #-}
expandFoldInRecord
:: forall rs as
. (as F.⊆ rs, V.RMap as)
=> F.Rec (FoldRecord F.ElField as) as
-> F.Rec (FoldRecord F.ElField rs) as
expandFoldInRecord = V.rmap (FoldRecord . FL.premap F.rcast . unFoldRecord)
{-# INLINABLE expandFoldInRecord #-}
class EndoFieldFoldsToRecordFolds rs where
endoFieldFoldsToRecordFolds :: F.Rec (FoldFieldEndo F.ElField) rs -> F.Rec (FoldRecord F.ElField rs) rs
instance EndoFieldFoldsToRecordFolds '[] where
endoFieldFoldsToRecordFolds _ = V.RNil
{-# INLINABLE endoFieldFoldsToRecordFolds #-}
instance (EndoFieldFoldsToRecordFolds rs, rs F.⊆ (r ': rs), V.RMap rs) => EndoFieldFoldsToRecordFolds (r ': rs) where
endoFieldFoldsToRecordFolds (fe V.:& fes) = FoldRecord (FL.premap (V.rget @r) (unFoldFieldEndo fe)) V.:& expandFoldInRecord @(r ': rs) (endoFieldFoldsToRecordFolds fes)
{-# INLINABLE endoFieldFoldsToRecordFolds #-}
sequenceRecFold
:: forall as rs
. F.Rec (FoldRecord F.ElField as) rs
-> FL.Fold (F.Record as) (F.Record rs)
sequenceRecFold = V.rtraverse unFoldRecord
{-# INLINABLE sequenceRecFold #-}
sequenceFieldEndoFolds
:: EndoFieldFoldsToRecordFolds rs
=> F.Rec (FoldFieldEndo F.ElField) rs
-> FL.Fold (F.Record rs) (F.Record rs)
sequenceFieldEndoFolds = sequenceRecFold . endoFieldFoldsToRecordFolds
{-# INLINABLE sequenceFieldEndoFolds #-}
liftFoldEndo :: V.KnownField t => FoldEndo t -> FoldFieldEndo F.ElField t
liftFoldEndo = FoldFieldEndo . fieldFold . unFoldEndo
{-# INLINABLE liftFoldEndo #-}
liftFolds
:: (V.RPureConstrained V.KnownField rs, V.RApply rs)
=> F.Rec FoldEndo rs
-> F.Rec (FoldFieldEndo F.ElField) rs
liftFolds = V.rapply liftedFs
where liftedFs = V.rpureConstrained @V.KnownField $ V.Lift liftFoldEndo
{-# INLINABLE liftFolds #-}
sequenceEndoFolds
:: forall rs
. ( V.RApply rs
, V.RPureConstrained V.KnownField rs
, EndoFieldFoldsToRecordFolds rs
)
=> F.Rec FoldEndo rs
-> FL.Fold (F.Record rs) (F.Record rs)
sequenceEndoFolds = sequenceFieldEndoFolds . liftFolds
{-# INLINABLE sequenceEndoFolds #-}
foldAll
:: ( V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs
)
=> (forall a . FL.Fold a a)
-> FL.Fold (F.Record rs) (F.Record rs)
foldAll f = sequenceEndoFolds $ V.rpureConstrained @V.KnownField (FoldEndo f)
{-# INLINABLE foldAll #-}
class (c (V.Snd t)) => ConstrainedField c t
instance (c (V.Snd t)) => ConstrainedField c t
type ConstrainedFoldable c rs = (V.RPureConstrained (ConstrainedField c) rs
, V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs
)
foldAllConstrained
:: forall c rs. ConstrainedFoldable c rs
=> (forall a . c a => FL.Fold a a)
-> FL.Fold (F.Record rs) (F.Record rs)
foldAllConstrained f =
sequenceEndoFolds $ V.rpureConstrained @(ConstrainedField c) (FoldEndo f)
{-# INLINABLE foldAllConstrained #-}
monoidWrapperToFold
:: forall f a . (N.Newtype (f a) a, Monoid (f a)) => FL.Fold a a
monoidWrapperToFold = FL.Fold (\w a -> N.pack a <> w) (mempty @(f a)) N.unpack
{-# INLINABLE monoidWrapperToFold #-}
class (N.Newtype (f a) a, Monoid (f a)) => MonoidalField f a
instance (N.Newtype (f a) a, Monoid (f a)) => MonoidalField f a
foldAllMonoid
:: forall f rs
. ( V.RPureConstrained (ConstrainedField (MonoidalField f)) rs
, V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs
)
=> FL.Fold (F.Record rs) (F.Record rs)
foldAllMonoid = foldAllConstrained @(MonoidalField f) $ monoidWrapperToFold @f
{-# INLINABLE foldAllMonoid #-}