{"id":27228580,"url":"https://github.com/tabdulradi/nullable","last_synced_at":"2025-04-10T12:38:13.702Z","repository":{"id":50778641,"uuid":"178596810","full_name":"tabdulradi/nullable","owner":"tabdulradi","description":"Makes `A | Null` work with for-comprehensions","archived":false,"fork":false,"pushed_at":"2024-02-11T22:29:12.000Z","size":41,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-02-11T23:28:08.203Z","etag":null,"topics":["allocation-cost","dotty","null-safety","scala","union-types"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tabdulradi.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}},"created_at":"2019-03-30T18:33:23.000Z","updated_at":"2024-02-11T22:33:29.000Z","dependencies_parsed_at":"2022-08-24T11:10:16.199Z","dependency_job_id":null,"html_url":"https://github.com/tabdulradi/nullable","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabdulradi%2Fnullable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabdulradi%2Fnullable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabdulradi%2Fnullable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabdulradi%2Fnullable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tabdulradi","download_url":"https://codeload.github.com/tabdulradi/nullable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217151,"owners_count":21066633,"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":["allocation-cost","dotty","null-safety","scala","union-types"],"created_at":"2025-04-10T12:38:13.121Z","updated_at":"2025-04-10T12:38:13.684Z","avatar_url":"https://github.com/tabdulradi.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nullable\n\nEnriches union types `A | Null` with an interface similar scala.Option making it usable inside for comprehensions\n\n## Usage\nAdd the following to your build.sbt\n```\nscalacOptions += \"-Yexplicit-nulls\"\nlibraryDependencies += \"com.abdulradi\" %% \"nullable-core\" % \"0.4.0\"\n```\n\n## Features\n### Plays nice with for comprehensions\n\n``` scala\nimport com.abdulradi.nullable.syntax.*\nval maybeA: Int | Null = 42\nval maybeB: String | Null = \"foo\"\n\n// Compiles fine\nfor \n  a \u003c- maybeA \nyield 5\n\n// Also compiles, no null pointer exceptions\nfor \n  a \u003c- maybeA \n  b \u003c- null\nyield ()\n\n// Compiles, and evaluates to null (because if condition doesn't match)\nfor \n  a \u003c- maybeA \n  if (a \u003c 0)\nyield a\n```\n\n### Familiar Option-like experience\n\n``` scala\nmaybeA.map(_ =\u003e 5)\nmaybeA.flatMap(_ =\u003e null)\nmaybeA.flatMap(_ =\u003e maybeB)\nmaybeA.fold(0)(_ + 1)\nmaybeA.getOrElse(0)\nmaybeA.contains(0)\nmaybeA.exists(_ == 0)\nmaybeA.toRight(\"Value was null\")\n```\n\n### Convert from/to Option\n\n``` scala\nval optA = Some(42)\nmaybeA.toOption == optA\nmaybeA == optA.orNull\n```\n\n### Prevents auto flattening\nWhile `Option[Option[A]]` is a valid type, the equivalent `A | Null | Null` is indistinguishable from `A | Null`. So, the library ensures that map and flatMap are used correctly used at compile time. \n\nThe following examples **don't compile**\n\n``` scala\nfor a \u003c- maybeA yield maybeB // Shows a compile time error suggesting to use flatMap instead\nmaybeA.flatMap(_ =\u003e 5) // Shows a compile time error suggesting message to use map instead\nmaybeA.map(_ =\u003e null) // Shows a compile time error suggesting to use flatMap instead\nmaybeA.map(_ =\u003e maybeB) // Shows a compile time error suggesting to use flatMap instead\n```\n\n#### Working with Generics\n\nThe following doesn't compile, as we can't prove `A` won't be `Null`\n\n``` scala\ndef useFlatMapWithNullableInScope[A](f: Int =\u003e A): A | Null = \n  maybeA.flatMap(f)\n```\n\nSolution: Propagate a Nullable instance and let the library check at usage site\n\n``` scala\ndef useFlatMapWithNullableInScope[A: Nullable](f: Int =\u003e A): A | Null = \n  maybeA.flatMap(f)\n\ndef useMapWithNotNullInScope[A: NotNull](f: Int =\u003e A): A | Null = \n  maybeA.map(f)\n```\n\n### Lightweight version\nIf you only care about for-comprehension features, but not the rest of Option-like methods, we also offer a lightweight syntax\n```scala\nimport com.abdulradi.nullable.forSyntax.*\n\nfor \n  a \u003c- maybeA\n  b \u003c- maybeB\n  res = a + b\n  if (res % 2) == 0\nyield res\n```\nThe rest of the methods like fold, getOrElse, etc won't be in scope.\n\n\n## License \u0026 Acknowledgements\n\nSince this library mimics the scala.Option behavior, it made sense to copy and adapt the documentation of it's methods and sometimes the implementation too. Those bits are copyrighted by EPFL and Lightbend under Apache License 2.0, which is the same license as this library.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftabdulradi%2Fnullable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftabdulradi%2Fnullable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftabdulradi%2Fnullable/lists"}