You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Right now, Functor is the only constraint with special consideration for VariantF. Whenever you build a VariantF with inj, it captures the Functor dictionary and keeps it around with the value. Since this is the defining evidence for VariantF, dispatching it is free since we always have the appropriate dictionary on hand. For anything else (Foldable, Traversable, etc) we have to collect all possible dictionaries at the call site, and perform a lookup to dispatch the appropriate dictionary. This is inefficient all around. It would be nice if we could let users inject arbitrary evidence whenever we build a VariantF, and use that to dispatch dictionaries.
The following is an example of some machinery that accomplishes this, but I'd like to get some feedback.
moduleDictTestwhereimportPreludeimportData.Eq (classEq1, eq1)
importData.Foldable (classFoldable, foldMap, foldl, foldr)
importData.Traversable (classTraversable, sequence, traverse)
newtypeFunctorDictf=FunctorDict
(forall a b. (a -> b) -> f a -> f b)
dataFoldableDictf=FoldableDict
(forall a r. (a -> r -> r) -> r -> f a -> r)
(forall a r. (r -> a -> r) -> r -> f a -> r)
(forall a r. Monoidr=> (a->r) ->fa->r)
dataTraversableDictf=TraversableDict
(forall a b m. Applicativem=> (a->mb) ->fa->m (fb))
(forall a m. Applicativem=>f (ma) ->m (fa))
newtypeEq1Dictf=Eq1Dict
(forall a. Eqa=>fa->fa->Boolean)
classGather (f :: Type->Type) e | e->fwheregather::efclassDict (f :: Type->Type) de | de->fwheredict::ef->dfdataBoxed (e :: (Type->Type) ->Type) (f :: Type->Type) a=Boxed (ef) (fa)
box::forallefa. Gatherfe=>fa->Boxedefa
box = Boxed gather
instancefunctorBoxed :: DictfFunctorDicte=>Functor (Boxedef) where
map k (Boxed e a) = case dict e ofFunctorDict map' ->Boxed e (map' k a)
instancefoldableBoxed :: DictfFoldableDicte=>Foldable (Boxedef) where
foldr a b (Boxed e c) = case dict e ofFoldableDict foldr' _ _ -> foldr' a b c
foldl a b (Boxed e c) = case dict e ofFoldableDict _ foldl' _ -> foldl' a b c
foldMap a (Boxed e b) = case dict e ofFoldableDict _ _ foldMap' -> foldMap' a b
instancetraversableBoxed ::
( Dict f TraversableDict e
, Dict f FoldableDict e
, Dict f FunctorDict e
) => Traversable (Boxed e f) where
traverse a (Boxed e b) = case dict e ofTraversableDict traverse' _ ->Boxed e <$> traverse' a b
sequence (Boxed e a) = case dict e ofTraversableDict _ sequence' ->Boxed e <$> sequence' a
instanceeq1Boxed :: DictfEq1Dicte=>Eq1 (Boxedef) where
eq1 (Boxed e a) (Boxed _ b) = case dict e ofEq1Dict eq1' -> eq1' a b
---newtypeEvidencef=Evidence{functor::FunctorDictf
, foldable::FoldableDictf
, traversable::TraversableDictf
, eq1::Eq1Dictf}instancegatherEvidence :: (Traversablef, Eq1f) =>GatherfEvidencewhere
gather = Evidence
{ functor: FunctorDict map
, foldable: FoldableDict foldr foldl foldMap
, traversable: TraversableDict traverse sequence
, eq1: Eq1Dict eq1
}
instanceevidenceFunctorDict :: DictfFunctorDictEvidencewhere
dict (Evidence { functor }) = functor
instanceevidenceEq1Dict :: DictfEq1DictEvidencewhere
dict (Evidence { eq1 }) = eq1
instanceevidenceFoldableDict :: DictfFoldableDictEvidencewhere
dict (Evidence { foldable }) = foldable
instanceevidenceTraversableDict :: DictfTraversableDictEvidencewhere
dict (Evidence { traversable }) = traversable
The Boxed type just packages up some evidence with some functor. If we extend that with a tag, then we'd have VariantFRep, and we could implement all the instance in terms of the user injected evidence. The smart constructor ensures that it's only every built using gather, which means that we always get the same dictionaries, so they are interchangeable in the case of binary operations like eq1.
I'm just wondering if anyone can think of any drawbacks or issues with this. I might setup a separate project purescript-typeclass-dicts or something to flesh it out more.
Right now,
Functor
is the only constraint with special consideration forVariantF
. Whenever you build aVariantF
withinj
, it captures theFunctor
dictionary and keeps it around with the value. Since this is the defining evidence forVariantF
, dispatching it is free since we always have the appropriate dictionary on hand. For anything else (Foldable
,Traversable
, etc) we have to collect all possible dictionaries at the call site, and perform a lookup to dispatch the appropriate dictionary. This is inefficient all around. It would be nice if we could let users inject arbitrary evidence whenever we build aVariantF
, and use that to dispatch dictionaries.The following is an example of some machinery that accomplishes this, but I'd like to get some feedback.
The
Boxed
type just packages up some evidence with some functor. If we extend that with a tag, then we'd haveVariantFRep
, and we could implement all the instance in terms of the user injected evidence. The smart constructor ensures that it's only every built usinggather
, which means that we always get the same dictionaries, so they are interchangeable in the case of binary operations likeeq1
./cc @MonoidMusician @joneshf
The text was updated successfully, but these errors were encountered: