{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# 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.General
(
EndoFold
, FoldEndo(..)
, FoldRecord(..)
, EndoFieldFoldsToRecordFolds
, ConstrainedField
, toFoldRecord
, recFieldF
, fieldToFieldFold
, sequenceRecFold
, sequenceEndoFolds
, foldAll
, foldAllConstrained
, functorFoldAllConstrained
, foldAllMonoid
)
where
import Frames.MapReduce.General ( RecGetFieldC(..)
, RCastC(..)
, IsoRec(..)
)
import Frames.Folds ( EndoFold
, FoldFieldEndo(..)
, monoidWrapperToFold
, MonoidalField
)
import qualified Control.Foldl as FL
import qualified Data.Profunctor as P
import qualified Data.Vinyl as V
import Data.Vinyl ( ElField )
import qualified Data.Vinyl.TypeLevel as V
import qualified Data.Vinyl.Functor as V
import qualified Frames as F
import Frames ( (:.) )
import qualified Frames.Melt as F
fieldFold
:: (Functor f, V.KnownField t)
=> EndoFold (f (V.Snd t))
-> EndoFold ((f :. ElField) t)
fieldFold =
P.dimap (fmap (\(V.Field x) -> x) . V.getCompose) (V.Compose . fmap V.Field)
{-# INLINABLE fieldFold #-}
newtype FoldEndo f t = FoldEndo { unFoldEndo :: EndoFold (f (V.Snd t)) }
newtype FoldRecord record f g rs a = FoldRecord { unFoldRecord :: FL.Fold (record (f :. ElField) rs) (g a) }
toFoldRecord
:: (a -> g b)
-> FL.Fold (record (f :. ElField) rs) a
-> FoldRecord record f g rs b
toFoldRecord wrap = FoldRecord . fmap wrap
{-# INLINABLE toFoldRecord #-}
filteredFold :: (f a -> Maybe a) -> FL.Fold a b -> FL.Fold (f a) b
filteredFold toMaybe (FL.Fold step begin done) = FL.Fold step' begin done
where step' x = maybe x (step x) . toMaybe
recFieldF
:: forall t rs a record f
. (V.KnownField t, Applicative f)
=> (forall x . f x -> Maybe x)
-> FL.Fold a (V.Snd t)
-> (record (f :. ElField) rs -> f a)
-> FoldRecord record f (f :. ElField) rs t
recFieldF toMaybe fld fromRecF = FoldRecord
$ P.dimap fromRecF (V.Compose . pure . V.Field) (filteredFold toMaybe fld)
{-# INLINABLE recFieldF #-}
fieldToFieldFold
:: forall x y rs record f
. ( V.KnownField x
, V.KnownField y
, F.ElemOf rs x
, RecGetFieldC x record f rs
, Applicative f
)
=> (forall z . f z -> Maybe z)
-> FL.Fold (V.Snd x) (V.Snd y)
-> FoldRecord record f (f :. ElField) rs y
fieldToFieldFold toMaybe fld = recFieldF toMaybe fld (rgetFieldF @x)
{-# INLINABLE fieldToFieldFold #-}
expandFoldInRecord
:: forall rs as record f
. (RCastC as rs record f, V.RMap as)
=> V.Rec (FoldRecord record f (f :. ElField) as) as
-> V.Rec (FoldRecord record f (f :. ElField) rs) as
expandFoldInRecord = V.rmap (FoldRecord . FL.premap rcastF . unFoldRecord)
{-# INLINABLE expandFoldInRecord #-}
class EndoFieldFoldsToRecordFolds rs record f where
endoFieldFoldsToRecordFolds :: F.Rec (FoldFieldEndo (f :. ElField)) rs -> F.Rec (FoldRecord record f (f :. ElField) rs) rs
instance EndoFieldFoldsToRecordFolds '[] record f where
endoFieldFoldsToRecordFolds _ = V.RNil
{-# INLINABLE endoFieldFoldsToRecordFolds #-}
instance (EndoFieldFoldsToRecordFolds rs record f
, RCastC rs (r ': rs) record f
, V.KnownField r
, RecGetFieldC r record f (r ': rs)
, V.RMap rs
) => EndoFieldFoldsToRecordFolds (r ': rs) record f where
endoFieldFoldsToRecordFolds (fe V.:& fes) = FoldRecord (FL.premap (rgetF @r) (unFoldFieldEndo fe)) V.:& expandFoldInRecord @(r ': rs) (endoFieldFoldsToRecordFolds fes)
{-# INLINABLE endoFieldFoldsToRecordFolds #-}
sequenceRecFold
:: forall as rs record f
. (IsoRec rs record f)
=> F.Rec (FoldRecord record f (f :. ElField) as) rs
-> FL.Fold (record (f :. ElField) as) (record (f :. ElField) rs)
sequenceRecFold = fmap fromRec . V.rtraverse unFoldRecord
{-# INLINABLE sequenceRecFold #-}
sequenceFieldEndoFolds
:: (EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f)
=> F.Rec (FoldFieldEndo (f :. ElField)) rs
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
sequenceFieldEndoFolds = sequenceRecFold . endoFieldFoldsToRecordFolds
{-# INLINABLE sequenceFieldEndoFolds #-}
liftFoldEndo
:: (V.KnownField t, Functor f)
=> FoldEndo f t
-> FoldFieldEndo (f :. ElField) t
liftFoldEndo = FoldFieldEndo . fieldFold . unFoldEndo
{-# INLINABLE liftFoldEndo #-}
liftFolds
:: (V.RPureConstrained V.KnownField rs, V.RApply rs, Functor f)
=> F.Rec (FoldEndo f) 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 record f
. ( V.RApply rs
, V.RPureConstrained V.KnownField rs
, EndoFieldFoldsToRecordFolds rs record f
, IsoRec rs record f
, Functor f
)
=> F.Rec (FoldEndo f) rs
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
sequenceEndoFolds = sequenceFieldEndoFolds . liftFolds
{-# INLINABLE sequenceEndoFolds #-}
foldAll
:: ( V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs record f
, IsoRec rs record f
, Functor f
)
=> (forall a . FL.Fold a a)
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) 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
foldAllConstrained
:: forall c rs record f
. ( V.RPureConstrained (ConstrainedField c) rs
, V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs record f
, IsoRec rs record f
, Applicative f
)
=> (forall a . f a -> Maybe a)
-> (forall a . c a => FL.Fold a a)
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
foldAllConstrained toMaybe f =
sequenceEndoFolds $ V.rpureConstrained @(ConstrainedField c)
(FoldEndo (fmap pure $ filteredFold toMaybe f))
{-# INLINABLE foldAllConstrained #-}
functorFoldAllConstrained
:: forall c rs record f
. ( V.RPureConstrained (ConstrainedField c) rs
, V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs record f
, IsoRec rs record f
, Applicative f
)
=> (forall a . c a => FL.Fold (f a) (f a))
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
functorFoldAllConstrained f =
sequenceEndoFolds $ V.rpureConstrained @(ConstrainedField c) (FoldEndo f)
{-# INLINABLE functorFoldAllConstrained #-}
foldAllMonoid
:: forall g rs record f
. ( V.RPureConstrained (ConstrainedField (MonoidalField g)) rs
, V.RPureConstrained V.KnownField rs
, V.RApply rs
, EndoFieldFoldsToRecordFolds rs record f
, IsoRec rs record f
, Applicative f
)
=> (forall a . f a -> Maybe a)
-> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
foldAllMonoid toMaybe =
foldAllConstrained @(MonoidalField g) toMaybe $ monoidWrapperToFold @g
{-# INLINABLE foldAllMonoid #-}