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

Consider a backpack-based implementation? #317

Open
mitchellwrosen opened this issue May 11, 2020 · 4 comments
Open

Consider a backpack-based implementation? #317

mitchellwrosen opened this issue May 11, 2020 · 4 comments

Comments

@mitchellwrosen
Copy link
Contributor

mitchellwrosen commented May 11, 2020

Hi, have you considered a backpack-based implementation of concurrency / dejafu? I think it'd be preferable to the typclass-based version (if it makes sense to do so).

One reason: even assuming specialization of all functions, some operations are just more efficient with base. For example, concurrency doesn't have access to the timer manager, so its implementation of registerDelay forks a thread.

I started down a path of writing a little concurrency library that internally was generic in MonadConc m, with functions exposed in an internal module for testing. The interface was specialized to IO in the user-facing part of the library.

But once I realized registerDelay forked a thread, I switched to using a signature instead, with just the bits I used

https://github.com/mitchellwrosen/trio/blob/d82595b643a39ad4678e4ae2c3bd6304f20cc06f/src/trio-indef/Trio/Sig.hsig

with two implementations, base and dejafu:

https://github.com/mitchellwrosen/trio/blob/d82595b643a39ad4678e4ae2c3bd6304f20cc06f/src/trio-impl-base/Trio/Sig/Base.hs

https://github.com/mitchellwrosen/trio/blob/d82595b643a39ad4678e4ae2c3bd6304f20cc06f/src/trio-impl-dejafu/Trio/Sig/Dejafu.hs

The wrapping was all pretty easy, and boilerplatey. It'd be nice to centralize its implementation somewhere :)

One thing I'm not sure of is the type-changing Program, depending on if you have setup or teardown. My test suite doesn't, so I didn't have to deal with that complication.

@mitchellwrosen
Copy link
Contributor Author

Oh, and another reason to prefer backpack: it cleans up type signatures a lot :)

Compare:

-- typeclass
newScope :: MonadConc m => m (TMVar (STM m) (Scope m))

-- backpack
newScope :: IO (TMVar Scope)

@barrucadu
Copy link
Owner

barrucadu commented May 11, 2020

This is something I've been interested in for a while (added to my to-do list in October 2018!) but I've just never got around to trying it out. Is there a good primer on backpack somewhere?

One thing I'm not sure of is the type-changing Program, depending on if you have setup or teardown. My test suite doesn't, so I didn't have to deal with that complication.

Yeah, keeping that feature would be good. It was originally added for a user who does some complex (but deterministic) set-up work at the start of their tests, and dejafu was spending way too much time working through that.

Previously I solved that problem with two functions, dontCheck and subconcurrency, but they had to be used in certain ways (eg, dontCheck had to be the first action of your program, subconcurrency could only be used once) or dejafu would throw an exception. The Program type put those conditions at the type level, preventing incorrect use.

I wonder if changing the representation a bit would cause any insurmountable problems:

data Program n a where
  ModelConc ::
    { runModelConc :: (a -> Action n) -> Action n
    } -> Program n a
  WithSetup ::
    { wsSetup   :: Program n x
    , wsProgram :: x -> Program n a
    } -> Program n a
  WithSetupAndTeardown ::
    { wstSetup    :: Program n x
    , wstProgram  :: x -> Program n y
    , wstTeardown :: x -> Either Condition y -> Program n a
    } -> Program n a

withTeardown would need to go (leaving only withSetup and withSetupAndTeardown, the functor/applicative/monad instances would get trickier, and dejafu would have to potentially make sense of nested setup and teardown actions; but it might be doable.

Or maybe they could be three separate types, and the functions in Test.DejaFu which consume a Program could be put into a typeclass.

@barrucadu
Copy link
Owner

Another thought: what about keeping MonadConc, and keeping dejafu written in terms of it, and having a concurrency-backpack package which defines the signature, and implementations in terms of IO and MonadConc?

Then for the Program stuff, you use the MonadConc implementation of the signature, and everything works like it currently does.

@mitchellwrosen
Copy link
Contributor Author

mitchellwrosen commented May 11, 2020

Is there a good primer on backpack somewhere?

I don't there's a great, concise primer. I found this wiki page which contains all the info you need, scattered about :) https://gitlab.haskell.org/ghc/ghc/-/wikis/backpack

I also had a peep at this cabal file when I ran into some trouble.

Another thought: what about keeping MonadConc, and keeping dejafu written in terms of it, and having a concurrency-backpack package which defines the signature, and implementations in terms of IO and MonadConc?

Then for the Program stuff, you use the MonadConc implementation of the signature, and everything works like it currently does.

Oh yeah, totally! It's only the version of the program that runs in IO that you want to code against a signature. The tests could (obviously) just use dejafu directly.

Edit: whoops, got a little confused for a second. I think you'd actually have trouble writing an implementation of the signature with polymorphic functions. Not sure how that would look 🤔

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