{"id":39584744,"url":"https://github.com/mperucca/constraints","last_synced_at":"2026-01-26T15:01:18.257Z","repository":{"id":158559765,"uuid":"575233908","full_name":"mperucca/constraints","owner":"mperucca","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-12T05:24:11.000Z","size":302,"stargazers_count":22,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-12-12T06:25:30.330Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mperucca.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,"publiccode":null,"codemeta":null}},"created_at":"2022-12-07T03:34:17.000Z","updated_at":"2024-12-12T05:24:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"5b961cac-cef8-44c5-b790-fe1ae753ce65","html_url":"https://github.com/mperucca/constraints","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mperucca/constraints","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mperucca%2Fconstraints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mperucca%2Fconstraints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mperucca%2Fconstraints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mperucca%2Fconstraints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mperucca","download_url":"https://codeload.github.com/mperucca/constraints/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mperucca%2Fconstraints/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28781308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T13:55:28.044Z","status":"ssl_error","status_checked_at":"2026-01-26T13:55:26.068Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":"2026-01-18T07:35:27.100Z","updated_at":"2026-01-26T15:01:18.251Z","avatar_url":"https://github.com/mperucca.png","language":"Scala","funding_links":[],"categories":["\u003ca name=\"Scala\"\u003e\u003c/a\u003eScala"],"sub_categories":[],"readme":"Guarantees\n===============\n\nUnderspecified APIs leave pitfalls for API users.\nThe goal of this library is to enable fully-specified APIs with ease.\n\nThe main idea is to specify constraints through `Guarantee`s. Suppose we want to provide an integer division function that enforces a non-zero divisor as well as no overflow. We can model that as follows:\n```scala 3\ndef divide(a: Int, b: Int)(using\n  noDivideByZero: Guarantee[b.type != 0],\n  noOverflow: Guarantee[a.type != Int.MinValue.type or b.type != -1]\n): Int = a / b\n```\nAttempts to call \u003ccode\u003edivide\u003c/code\u003e must provide `Guarantee`s which can be obtained through various ways:\n* `trust` which just constructs one without checking\n* \u003ccode\u003etest\u003c/code\u003e which checks at runtime through `Compute.To[Boolean]` type class instances\n* `apply` which attempts to verify the constraint at compile time (still under development)\n\nHere's how a caller could call `divide` safely when working with runtime values (presumably the most common scenario):\n```scala 3\nval a, b = Random.nextInt()\n\nGuarantee.test[b.type != 0 and (a.type != Int.MinValue.type or b.type != -1)]\n  .branch(\n    ifHolds = divide(a, b) // compiles because the necessary Guarantee is in scope\n    ifFails = divide(a, b) // compile error since the Guarantee in scope is inverted\n  )\n```\n`Guarantee.test[Constraint]` returns `Either[Guarantee[Not[Constraint]], Guarantee[Constraint]]` following the convention of the `Right` side being successful.\nAt runtime, the constraint check will run, and only if the `Guarantee` is acquired through the `Right` case are we able to call `divide` on the `ifHolds` contextual callback parameter of the extension method `branch` without a compile error.\nThough invisible in this code, `ifHolds` has an implicit `Guarantee[b.type != 0 and (a.type != Int.MinValue.type or b.type != -1)]` in scope. This satisfies both the necessary `noDivideByZero` and `noOverflow` implicit parameters to `divide` due to `and` being translated into the intersection type `\u0026`.\n\nOne neat thing about `Guarantee` is that it knows DeMorgan's laws and represents Boolean predicates in their simplest forms. This means the type system can tell that the following examples type check:\n```scala 3\nsummon[\n  Guarantee[b.type == 0 or (a.type == Int.MinValue.type and b.type == -1)]\n    =:=\n  Guarantee[Not[b.type != 0 and (a.type != Int.MinValue.type or b.type != -1)]]\n]\n\nsummon[\n  Guarantee[b.type != 0 and (a.type != Int.MinValue.type or b.type != -1)]\n    \u003c:\u003c\n  Guarantee[b.type != 0]\n]\n\nsummon[\n  Guarantee[b.type != 0 and (a.type != Int.MinValue.type or b.type != -1)]\n    \u003c:\u003c\n  Guarantee[a.type != Int.MinValue.type or b.type != -1]\n]\n```\nThe types `==`, `!=`, and other common functions, as well as the combinators `and`, `or`, `Not`, and others as well are provided by the library.\n\nLet's look at another example of how to safely merge two sorted lists.\n\nThere are many ways to sort a list, so merging two lists requires the lists be sorted the same way. Here's an unsafe merge than relies on, but doesn't enforce, this sorted condition to merge sorted lists in linear time.\n\n```scala 3\ndef mergeUnsafe[A](list1: List[A], list2: List[A])(using comp: Ordering[A]): List[A] =\n  (list1, list2) match\n    case (Nil, l2) =\u003e l2\n    case (l1, Nil) =\u003e l1\n    case (l1 @ h1 :: t1, l2 @ h2 :: t2) =\u003e\n      if comp.lt(h1, h2)\n      then h1 :: mergeByUnsafe(t1, l2)\n      else h2 :: mergeByUnsafe(l1, t2)\n```\n\n`list1` and `list2` must already sorted in the same way for `mergeUnsafe` to work.\nHere's how a safer alternative might be represented that enforces this constraint:\n\n```scala 3\ndef merge[A](list1: List[A], list2: List[A])(using comp: Ordering[A])(\n  using Guarantee[Sorted[comp.type, list1.type]], Guarantee[Sorted[comp.type, list2.type]]\n): SortedList[A, comp.type] =\n  Guaranteed.Refined(mergeUnsafe(list1, list2))(Guarantee.trust)\n\n// Represents the constraint that the comparator C has sorted L\ntype Sorted[C, L]\n\n// A SortedList is a List, but it also carries a Guarantee that it's sorted\ntype SortedList[A, C] = Guaranteed.Refined[List[A], [L] =\u003e\u003e Sorted[C, L]]\n\n// Sorts a list with the standard library, but it also attaches the sorted guarantee\ndef sort[A](list: List[A])(using comp: Ordering[A]): SortedList[A, comp.type] =\n  Guaranteed.Refined(list.sorted)(Guarantee.trust)\n```\nWe can use this function as follows:\n```scala 3\n// sort the lists\nval list1, list2 = sort(List.fill(3)(Random.nextInt(9)))\n// bring the sorted list guarantees into scope\nimport list1.guarantee\nimport list2.guarantee\n// merge the sorted lists together\nmerge(list1.value, list2.value)\n\n// sort another list differently\nval list3 = sort(List.fill(3)(Random.nextInt(9)))(using Ordering.Int.reverse)\nimport list3.guarantee\nmerge(list1.value, list3.value) // compile error\n```\nUsing `List[Int]`s means the implicit `Ordering.Int` instance will be used. Sorting one of the lists instead by `Ordering.Int.reverse` produces a compile error.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmperucca%2Fconstraints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmperucca%2Fconstraints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmperucca%2Fconstraints/lists"}