Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for capability-style records #19

Open
schell opened this issue Sep 20, 2019 · 1 comment
Open

Support for capability-style records #19

schell opened this issue Sep 20, 2019 · 1 comment

Comments

@schell
Copy link

schell commented Sep 20, 2019

I really don't know if this is possible, but I'm referring to capability records like:

data LogCap (m :: Type -> Type) 
  = LogCap 
  { logInfo :: String -> String -> m ()
  , logError :: String -> String -> m () 
  } deriving Generic

From the outside these records look like HKDs - every field contains the type variable, but you might consider function parameters being "hidden" from it (one of the disclaimers from ProductB).

The first problem to solve is the FunctorB instance, as GFunctorB doesn't cut it:

• No instance for (barbies-1.1.3.0:Data.Barbie.Internal.Functor.GFunctorB
                         *
                         *
                         f
                         g
                         (Rec
                            *
                            (String
                             -> String
                             -> barbies-1.1.3.0:Data.Generics.GenericN.Param * * 0 f ())
                            (String -> String -> f ()))
                         (Rec
                            *
                            (String
                             -> String
                             -> barbies-1.1.3.0:Data.Generics.GenericN.Param * * 0 g ())
                            (String -> String -> g ())))

Intuitively though, it seems that we should be able to traverse an arrow with (f a -> g a), drilling deeper by creating lambdas until we come to an m a.

The point of all this is that at the end of the day it would be nice to be able to write a generic lift function for any of these forms of HKDT.

Given:

newtype Apply (f :: Type -> Type) (g :: Type -> Type) a
  = Apply { runApply :: f a -> g a }


bapply
  :: ProductB b
  => b (Apply i o)
  -> b i
  -> b o
bapply =
  bzipWith runApply

You could easily do something like:

let 
  ioLogCap :: LogCap IO 
  ioLogCap = ioLoggingCapabilities 

  resourceTLogCap :: LogCap (ResourceT IO) 
  resourceTLogCap = bapply (buniq $ Apply lift) ioLogCap
in 
  runResourceT $ f resourceTLogCap

which could kill a pleasant amount of boilerplate when using capability records.

Thoughts? I might be crazy ¯_(ツ)_/¯

@jcpetruzza
Copy link
Owner

I think you are definitely right, and we should be able to handle this case. Conceptually, the main thing that we are missing is to allow the functor argument (e.g. m in LogCap m) to occur under another functor when deriving FunctorB (and similarly for the other classes). At the moment we allow a barbie type to occur under a functor (e.g. Maybe (LogCap m)), but not the functor argument itself. This shouldn't be hard to fix.

Because we have an instance Functor (-> r), this addition alone would let us handle simpler capability records such as:

data LogCap (m :: Type -> Type)
  { logInfo  :: String -> m ()
  , logError :: String -> m ()
  }
  deriving (Generic)

instance FunctorB LogCap

Of course, one would like to handle functions with an arbitrary number of parameters (not just one) or, more generally, an unlimited number of nested functors. This is in fact a bit tricky; but ultimately doable, I think (e.g., one could identify arbitrary chains of functor applications in the generic representation of the type, coerce the type into a version where all the functors were smashed into one using Compose, operate on that single functor, and then coerce it back to the original representation).

A quicker workaround, would be to do a manual unrolling defining k instances of the form "the functor argument occurs under {1,2,3,...k} functors".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants