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

api change proposal #122

Open
chessai opened this issue Dec 30, 2020 · 1 comment
Open

api change proposal #122

chessai opened this issue Dec 30, 2020 · 1 comment

Comments

@chessai
Copy link
Collaborator

chessai commented Dec 30, 2020

API noise might be able to be cut.

Proxy noise

we should drop support for GHC 7.x. primitive is going to do this soon. This has a few benefits:

  1. reduced maintenance burden (less CPP, hopefully!)
  2. we an use -XTypeApplications, cleaning up code

Laws noise

so, most of the time, i am creating laws, and immediately consuming them. as a user, that categorises them to me as noise, and not something useful - which goes against one of the core tenets of this library: the least amount of new types and concepts for end users as possible. How can we remedy this?

Well, if I take a look, all of my tests with this library fit in the following categories:

  1. lawsCheckMany, in a test suite
  2. lawsCheck, in ghci

I never transform laws, I always produce them and immediately consume them.
All I really want from lawsCheck functions is to give them a type and a set of laws, have it run the tests in IO, printing results as it goes.

So, why not something like the following rough sketch

eq :: forall a. (Eq a, Show a, Arbitrary a) => IO ()
eq = lawsCheck (eqLaws @a)

describe :: String -> IO () -> IO ()
describe section test = do
  putStrLn section -- prettify this in some way. this sketch just shows intent.
  test

and you can use it like so:

main :: IO ()
main = do
  describe "Set Int" $ do
    eq @(Set Int)
    ord @(Set Int)
    monoid @(Set Int)
  describe "Map Int Int" $ do
    eq @(Map Int Int)
    ord @(Map Int Int)

I'm not sure if this is the best way of accomplishing my stated goal of eliminating Laws from users' code, but it is a way.

Keep in mind that I think we've already done a fantastic job cutting down on API cruft from the status quo, and the API as it is today is still very simple. You have a type, Laws, a way to produce Laws, and a way to consume Laws. But in my code I always find that most of the time, consumption immediately following production is a smell.

@andrewthad
Copy link
Owner

The Proxy stuff should definitely go away. Once primitive drops the old GHCs that need them, we should remove the Proxy compatibility types from this library.

As far as the Laws-related changes goes, I typically run property tests a little differently than the way you describe running them. I use tasty for almost every test suite I write, and I have a copy of this function in all of my test suites:

lawsToTest :: QCC.Laws -> TestTree
lawsToTest (QCC.Laws name pairs) = testGroup name (map (uncurry TQC.testProperty) pairs)

I should probably write a tasty-quickcheck-classes to cut down on some of this duplication, but I've never bothered to do it. You can see lawsToTest in action in primitive, in test/main.hs. Elsewhere in that test suite is:

, testGroup "SmallArray"
  [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int)))
  , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int)))
  , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int)))
  , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))

So the Laws abstraction itself is useful because it makes quickcheck-classes agnostic to the test framework (the one I used to use before tasty, the only other one I'm actually aware of, is named test-framework). However, even though I don't want to cut Laws out of the API (I'm not sure if this was being suggested or not), I do think there is a good argument for including these convenience functions you mention in the library. The ones for running a single suite of property tests are straightforward. You wrote:

eq :: forall a. (Eq a, Show a, Arbitrary a) => IO ()
eq = lawsCheck (eqLaws @a)

We could put this and all the others like it in the library at Test.QuickCheck.Classes.Run. I'm not sure about convenience functions around lawsCheckMany though. I just don't know what they would look like, but you might have some idea.

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