{"id":18695016,"url":"https://github.com/rybla/liquidhaskell-metaprogramming","last_synced_at":"2025-11-08T12:30:32.263Z","repository":{"id":81618217,"uuid":"457891295","full_name":"rybla/liquidhaskell-metaprogramming","owner":"rybla","description":null,"archived":false,"fork":false,"pushed_at":"2022-04-24T20:53:52.000Z","size":276,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-12-28T03:17:06.347Z","etag":null,"topics":[],"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/rybla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-02-10T18:01:12.000Z","updated_at":"2022-02-10T18:08:09.000Z","dependencies_parsed_at":"2023-03-06T01:15:21.135Z","dependency_job_id":null,"html_url":"https://github.com/rybla/liquidhaskell-metaprogramming","commit_stats":null,"previous_names":["rybla/liquidhaskell-metaprogramming"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rybla%2Fliquidhaskell-metaprogramming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rybla%2Fliquidhaskell-metaprogramming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rybla%2Fliquidhaskell-metaprogramming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rybla%2Fliquidhaskell-metaprogramming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rybla","download_url":"https://codeload.github.com/rybla/liquidhaskell-metaprogramming/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239552764,"owners_count":19657983,"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":[],"created_at":"2024-11-07T11:13:39.783Z","updated_at":"2025-11-08T12:30:32.201Z","avatar_url":"https://github.com/rybla.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# liquidhaskell-metaprogramming\n\nLiquidHaskell is a plugin for Haskell that introduces decidable refinement types\n(liquid types) to Haskell's type system. The user writes refinement type\nspecifications and LiquidHaskell's typechecker will use an SMT solver (e.g. Z3)\nto check that that the user's implementations in fact satisfy those\nspecifications.\n\nRefinement types are a subsystem of dependent type theory, where each refinement\ntype has the form\n\n```\n{x:a | p}\n```\n\nwhere `a` is a Haskell type and `p` is a boolean expression that can refer to\nany variables in scope (including x). Theses are often called \"subset types\" in\ndependent type theory, since it can be read as \"the subset of `a` that satisfies\n`p`\". For example\n\n```\nx:{Int | 0 \u003c= x} -\u003e y:{Int | x \u003c y} -\u003e z:{Int | x \u003c= z \u0026\u0026 z \u003c y}\n```\n\nis the refinement type of a function that takes takes two integers and outputs\nan integer between them.\n\n## Lack of Explicit Refinement Types\n\nAlthough Haskell has implicit polymorphism, it is possible to make the\npolymorphism explicit using the `ExplicitForAll` and `TypeApplication` language\nextensions:\n\n```\n-- implicit\nid :: a -\u003e a\nid x = x\n\n-- explicit type quantification\nid :: forall a. a -\u003e a\nid x = x\n\n-- explicit type application\ntest :: Int -\u003e Int\ntest = id @Int\n```\n\nThe same cannot exactly be said for refinement types. Since LiquidHaskell builds\nstrictlly on top of Haskell, it is impossible to reference refinement types in\nnormal Haskell code. There are a few practical consequences of this:\n\n- no implicit parameters\n- no branching conditional on refinements\n\n### Consequence: No Implicit Parameters\n\n\u003c!-- TODO: come up with another equivalence relation example --\u003e\n\u003c!-- https://math.stackexchange.com/a/2790998 --\u003e\n\nIn most languages that support dependent type theory, term arguments can often\nbe inferred in the same way that Haskell infers type arguments for the sake of\nimplicit polymorphism. In Agda for example:\n\n```\ncommutativity : forall m n -\u003e m + n = n + m\ncommutativity m n = ...\n\n1+2=2+1 : 1 + 2 = 2 + 1\n1+2=2+1 = commutativity _ _\n```\n\nJust from the type that `commutativity _ _` is expected to have, its arguments\ncan be inferred and so can just be given as underscores (which mean \"infer this\nif possible, otherwise throw a type error\").\n\nThis is not generally possible in LiquidHaskell because refinement types are\nmore amorphous. A term does not have a unique refinement type, since it also has\nany refinement type that is implied by the refinement type it originally started\nout with. Additionally, as more refinements come into context, those must be\naccounted for as well in it's refinement type. For example\n\n```\nx:{Int | 0 \u003c= x} -\u003e y:{Int | x \u003c= y} -\u003e ...\n```\n\nBefore `y` comes into scope, the refinement of `x` is just `0 \u003c= x`, but as soon\nas `y` comes into scope that refinement now contains `x \u003c= y` as well. This\nwould all have to be handled manually if it were using strict subset types in\ndependent type theory, but LiquidHaskell avoids this hassle by relying on an SMT\nsolver to put all the refinement together and figure out if they're satisfied.\n\nThis lacking leads to a lot of redundancy in LiquidHaskell code where\nshould-have-been implicit arguments have to be given many times. For example:\n\n```\nr :: a -\u003e a -\u003e Bool\nr x y = ...\n\ntransitivity :: x:a -\u003e y:{a | r x y} -\u003e z:{a | r y z} -\u003e z:{a | r x z}\ntransitivity x y z = ...\n\nchain :: {r x v}\nchain =\n  transitivity x (y `by` ...)\n    (transitivity y (z `by` ...)\n      (transitivity z (w `by` ...) (v `by` ...)))\n```\n\nHere, we need to repeat `y` and `z` twice each.\n\nMacros can be used to splice the same expressions into multiple places when\nneeded.\n\n### Consequence: No Branching Conditional on Refinements\n\n### Consequence: No Expansion of Unrefined Functions\n\nSince refinement types only exist at the type level, any refinement-relevant\ninformation of a term is only accessibly nonlocally if it is included in the\ntype.\n\nMacros can be used to slice a template into an expression to take advantage of\nlocal refinement-relevant information without the hastle of providing the whole\ncontext to a function call.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frybla%2Fliquidhaskell-metaprogramming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frybla%2Fliquidhaskell-metaprogramming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frybla%2Fliquidhaskell-metaprogramming/lists"}