{"id":16313955,"url":"https://github.com/lysxia/test-fun","last_synced_at":"2025-10-25T16:31:52.239Z","repository":{"id":56880159,"uuid":"222170900","full_name":"Lysxia/test-fun","owner":"Lysxia","description":"Representation of higher-order functions for property testing","archived":false,"fork":false,"pushed_at":"2020-02-24T05:10:35.000Z","size":85,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-31T09:22:02.755Z","etag":null,"topics":["functional-programming","random","testing"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lysxia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-16T23:12:09.000Z","updated_at":"2024-03-10T20:29:57.000Z","dependencies_parsed_at":"2022-08-20T23:40:34.849Z","dependency_job_id":null,"html_url":"https://github.com/Lysxia/test-fun","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lysxia%2Ftest-fun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lysxia%2Ftest-fun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lysxia%2Ftest-fun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lysxia%2Ftest-fun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lysxia","download_url":"https://codeload.github.com/Lysxia/test-fun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238183467,"owners_count":19430123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","random","testing"],"created_at":"2024-10-10T21:52:46.792Z","updated_at":"2025-10-25T16:31:51.860Z","avatar_url":"https://github.com/Lysxia.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Testable functions [![Hackage](https://img.shields.io/hackage/v/test-fun.svg)](https://hackage.haskell.org/package/test-fun) [![Build Status](https://travis-ci.com/Lysxia/test-fun.svg)](https://travis-ci.com/Lysxia/test-fun)\n\n\nA representation of functions for property testing, featuring\nrandom generation, shrinking, and printing.\n\nThis package implements the core functionality.\nSeparate packages integrate it with existing testing frameworks.\n\n- QuickCheck: [quickcheck-higherorder](https://github.com/Lysxia/quickcheck-higherorder)\n\n- Hedgehog: [hedgehog-higherorder](https://github.com/Lysxia/hedgehog-higherorder)\n\n## Summary\n\nThis package defines a type of *testable functions* `a :-\u003e r`,\nrepresenting functions `a -\u003e r`.\n\n- To interpret a testable function into a function `a -\u003e r`,\n  use `applyFun :: (a :-\u003e r) -\u003e a -\u003e r`.\n\n- To pretty-print a testable function,\n  use `show :: Show r =\u003e (a :-\u003e r) -\u003e String`.\n\n- To shrink a testable function, given a shrinker for `r`,\n  use `shrinkFun :: (r -\u003e [r]) -\u003e (a :-\u003e r) -\u003e [a :-\u003e r]`.\n\n- To randomly generate a testable function `a :-\u003e r`,\n  apply a *cogenerator* of `a` to a generator of `r`.\n  Cogenerators can be defined using combinators from this library.\n\n### Cogenerators\n\nThe type of cogenerators of `a` is `Co Gen a r`,\nwhere `Gen` is QuickCheck's monad of random generators\nand `r` is an abstract parameter (it's really `forall r. Co Gen a r`).\n\nThat type `Co Gen a r` is literally defined as a type synonym of\n`Gen r -\u003e Gen (a :-\u003e r)`.\nGiven both a cogenerator `c :: Co Gen a r`, and a generator `g :: Gen r`,\nwe can construct the generator of testable functions `c g :: Gen (a :-\u003e r)`.\n\n(Users can just think of `Co Gen` as a whole,\neven though the implementation defines a more general `Co`\nwhich may be applied to any monad.\nSimilarly, the parameter `r` can be ignored most of the time;\nit matters to cogenerators of parameterized types.)\n\nThere are several combinators to define cogenerators,\ncovering the following scenarios.\n\n#### Newtypes and embeddings\n\nIf we have a newtype `A` around some old type `B`, and we also have\na cogenerator of `B`:\n\n```haskell\nnewtype A = MkA { unA :: B }\n\ncogenB :: Co Gen B r\n```\n\nThen `cogenEmbed` transforms `cogenB` into a cogenerator of `A`:\n\n```haskell\ncogenEmbed \"unA\" unA cogenB :: Co Gen A r\n```\n\nThis is actually not restricted to newtypes:\nany \"embedding\" function `A -\u003e B` (here, `unA`) can be used to convert a\n`Co Gen B r` to a `Co Gen A r`.\n(Yes, there is a contravariant functor hiding there.)\nNote that `cogenEmbed` expects a name for that function as a `String`\nin its first argument, for pretty-printing.\n\n#### Generic data types\n\nTo define a cogenerator of a type which is an instance of `Generic` (from\n`GHC.Generics`), use `cogenGeneric`. For example, consider this type:\n\n```haskell\ndata Small a = Zero | One a | Two a a\n  deriving Generic\n```\n\nThe function `cogenGeneric` takes a heterogeneous list of\ncogenerators, one for each constructor of the generic type.\nThis is `cs` in the example below.\n\nThe heterogeneous list is constructed using `(:+)` to append\nelements and `()` for the end of the list.\n\nFor constructors with multiple fields,\nuse `(.)` to compose cogenerators for individual fields.\n\nFor nullary constructors, use `id` as the \"nullary cogenerator\".\n\n```haskell\ncogenSmall ::\n  forall a.\n  (forall r. Co Gen a r) -\u003e\n  (forall r. Co Gen (Small a) r)\ncogenSmall cogenA = cogenGeneric cs where\n  cs\n    =  id                 -- Nullary cogenerator, for the constructor Zero\n    :+ cogenA             -- Cogenerator of a, for the constructor One\n    :+ (cogenA . cogenA)  -- A cogenerator of a, once for each field of the constructor Two\n    :+ ()                 -- End of the list\n```\n\n#### Functions\n\nTo generate higher-order testable functions `(a -\u003e b) :-\u003e r`,\nwe need a cogenerator of functions `a -\u003e b`,\nwhich we can define using `cogenFun`.\n\nTo a first approximation, the function `cogenFun` transforms\na cogenerator of `b` into a cogenerator of `(a -\u003e b)`, provided\na way to generate, shrink, and show `a`.\n\nThis is actually generalized further by allowing one to provide\na way to generate, shrink, and show a *representation* `a0` of `a`,\nwhich can be equal to `a` in simple cases,\nbut this generalization makes it possible to generate\nfunctions of arbitrarily high order.\n\nHence, to construct a cogenerator of `a -\u003e b`,\nthe function `cogenFun` takes the following arguments, in this order:\n\n1. `Concrete a0`: a dictionary containing a shrinker and a pretty-printer of\n   representations `a0`;\n2. `Gen (Maybe a0)`: a random generator of `a0`, it must generate `Nothing`\n   once in a while (say with probability 1/5 if you have no clue);\n3. `a0 -\u003e a`: a function from representations to actual values\n   (`id` in simple cases);\n4. `forall r. Co Gen b r`: a cogenerator of `b`.\n\n## References\n\n- [*Shrinking and showing functions*](https://dl.acm.org/citation.cfm?id=2364516),\n  by Koen Claessen, Haskell Symposium 2012.\n\n  This package extends that work with support for higher-order functions.\n\n  Other implementations based on that paper can be found in:\n\n  + [QuickCheck](https://hackage.haskell.org/package/QuickCheck-2.13.2); in\n    particular see the [`Fun`\n    type](https://hackage.haskell.org/package/QuickCheck-2.13.2/docs/Test-QuickCheck.html#g:14).\n\n  + [hedgehog-fn](https://hackage.haskell.org/package/hedgehog-fn).\n\n## Internal module policy\n\nModules under `Test.Fun.Internal` are not subject to any versioning policy.\nBreaking changes may apply to them at any time.\n\nIf something in those modules seems useful, please report it or create a pull\nrequest to export it from an external module.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flysxia%2Ftest-fun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flysxia%2Ftest-fun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flysxia%2Ftest-fun/lists"}