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

Effect system integrations #4

Open
cdsmith opened this issue Jun 30, 2021 · 3 comments
Open

Effect system integrations #4

cdsmith opened this issue Jun 30, 2021 · 3 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@cdsmith
Copy link
Owner

cdsmith commented Jun 30, 2021

Migrated from TODO file.

There are a lot of libraries in Haskell that can be used to define an API to some external system. Ideally, there would be a way to mock all of these with HMock. This includes:

  • Effect systems like polysemy, fused-effects, freer-simple, and eff.
  • API layers, like haxl and servant

This needs some serious thought about how to separate mtl-isms from the more reusable core of HMock, so that as much as reasonable can be shared with other systems. I suppose the right way to go about this is to take a few examples, ask how you'd reimplement HMock for that specific system, and then look for the right refactorings to share the common bits. The HMock core expectation language, surface expectation language, and most of the MockableBase class should hopefully be shared.

Many of these systems already have their own action types, which could be trivially wrapped rather than deriving a new Action class. This might mean that Action should be an injective type family rather than a data family. However, we might need to lose some type safety for this to occur, since the method name isn't encoded into the type any longer. This definitely might interfere with any type tricks of the sort I'm contemplating to implement polymorphic return values, too.

@cdsmith cdsmith added enhancement New feature or request help wanted Extra attention is needed labels Jun 30, 2021
@cdsmith cdsmith changed the title Lots of library integrations Effect system integrations Sep 8, 2021
@cdsmith
Copy link
Owner Author

cdsmith commented Sep 8, 2021

Since I've split servant and haxl into their own issues, I'm claiming this issue for effect system integrations. This includes:

  • polysemy
  • fused-effects
  • freer-simple
  • eff

and perhaps others.

@cdsmith
Copy link
Owner Author

cdsmith commented Sep 8, 2021

I imagine this will eventually look something like the following:

class MonadFoo where
    mtlFoo :: Int -> m ()

data FooEffect m a where
    PolysemyFoo :: Int -> FooEffect m ()

data EffectSystem = MTLStyle | Polysemy | FusedEffects | Eff | ...

type family EffectType ... where
  EffectType MTLStyle = (Type -> Type) -> Constraint
  EffectType Polysemy = (Type -> Type) -> (Type -> Type)

class Mockable (sys :: EffectSystem) (eff :: EffectType sys) | eff -> sys where ...

-- Instances, usually written by Template Haskell
instance MockableBase MTLStyle MonadFoo where
  data Action MTLStyle MonadFoo (name :: Symbol) (m :: Type -> Type) (a :: Type) where
    MtlFoo :: Int -> Action MTLStyle MonadFoo "mtlFoo" m ()

  data Matcher MTLStyle MonadFoo (name :: Symbol) (m :: Type -> Type) (a :: Type) where
    MtlFoo_ :: Predicate Int -> Matcher MTLStyle MonadFoo "mtlFoo" m ()

  ...

instance MockableBase Polysemy FooEffect where
  data Action Polysemy FooEffect ...
    -- open question: reuse the GADT FooEffect?  Or define a new Action GADT?
    -- Advantage to #1: Seems more natural for polysemy.  Fewer name conflicts.
    -- Advantage to #2: We get the operation name in the type for type-safety.

  data Matcher Polysemy FooEffect ... where
    PolysemyFoo_ :: Predicate Int -> Matcher Polysemy FooEffect ...

-- Template Haskell generators can just take the effect system as a value-level argument.
makeMockable MTLStyle [t| MonadFoo |]
makeMockable Polysemy [t| FooEffect |]

-- Runner for MTLStyle
instance MonadFoo MockT where ...

-- Runner for polysemy - can this be polymorphic in the effect?  Probably not.
mockFooEffect :: Has MockEffect r => Sem (FooEffect : r) a -> Sem r a
interpretMocks :: ... => Sem (MockEffect : r) a -> Sem r a

@cdsmith
Copy link
Owner Author

cdsmith commented Sep 8, 2021

There are currently a lot of dependency circles involving MockT in HMock's implementation, so the real trick here is going to be finding the best way to disentangle all of this stuff that shares as much of the test writer experience as possible while still working for different effect systems. In particular, we need to be able to share the expectation combinators: expect, expectN, expectAny, inSequence, and so on. Since these depend on Rule, this probably means that Rule needs to incorporate a type that depends on the effect system, as well.

I have deliberately left HMock in a pre-1.0 version number, and warned people to use upper bounds on dependencies, in anticipation of needing to make significant changes so that this will work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant