{"id":18098541,"url":"https://github.com/makiftutuncu/as","last_synced_at":"2025-04-06T04:18:01.336Z","repository":{"id":152869397,"uuid":"263572077","full_name":"makiftutuncu/as","owner":"makiftutuncu","description":"No-macro, no-reflection, opinionated type refinement library for Scala 3","archived":false,"fork":false,"pushed_at":"2025-03-17T06:30:35.000Z","size":33,"stargazers_count":1,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T07:35:09.089Z","etag":null,"topics":["refinement","refinement-types","sbt","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","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/makiftutuncu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2020-05-13T08:35:25.000Z","updated_at":"2024-05-24T18:27:05.000Z","dependencies_parsed_at":"2024-09-16T06:27:21.078Z","dependency_job_id":"df893564-c109-4c96-b0ff-a8237d6637a5","html_url":"https://github.com/makiftutuncu/as","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makiftutuncu%2Fas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makiftutuncu%2Fas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makiftutuncu%2Fas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makiftutuncu%2Fas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/makiftutuncu","download_url":"https://codeload.github.com/makiftutuncu/as/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247430976,"owners_count":20937876,"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":["refinement","refinement-types","sbt","scala"],"created_at":"2024-10-31T20:11:40.883Z","updated_at":"2025-04-06T04:18:01.317Z","avatar_url":"https://github.com/makiftutuncu.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# as [![](https://img.shields.io/badge/scaladoc-1.0.1-brightgreen.svg?style=for-the-badge\u0026logo=scala\u0026color=dc322f\u0026labelColor=333333)](https://javadoc.io/doc/dev.akif/as_3)\n\nas is a no-macro, no-reflection, opinionated type refinement library for Scala 3. It is powered by [e](https://github.com/makiftutuncu/e) to handle invalid value errors.\n\n| Latest Version | Java Version | Scala Version |\n|----------------|--------------|---------------|\n| 1.0.1          | 21           | 3.4.2         |\n\n## Table of Contents\n\n1. [Installation](#installation)\n2. [How to Use](#how-to-use)\n3. [Development and Testing](#development-and-testing)\n4. [Releases](#releases)\n5. [Contributing](#contributing)\n6. [License](#license)\n\n## Installation\nIf you use SBT, add following to your `build.sbt`:\n\n```scala 3\nlibraryDependencies += \"dev.akif\" %% \"as\" % \"1.0.1\"\n```\n\nIf you use Maven, add following to your `pom.xml`:\n\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003edev.akif\u003c/groupId\u003e\n        \u003cartifactId\u003eas_3\u003c/artifactId\u003e\n        \u003cversion\u003e1.0.1\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\nIf you use Gradle, add following to your project's `build.gradle`:\n\n```javascript\ndependencies\n{\n    implementation('dev.akif:as_3:1.0.1')\n}\n```\n\n## How to Use\n\nAssume we have following `Author` and `Book` types.\n\n```scala 3\ncase class Author(id: Long, name: String)\n\ncase class Book(id: Long, authorId: Long, name: String)\n```\n\n`Long` and `String` are primitives and may not contain valid values for ids or names. Even if you implement ids and names with their own types to guard against invalid values, author's id is different from a book's id, and they should not be of same type.\n\nTo overcome this, we can have separate **refined** types.\n\nHere is how you can create one, refining `Long` as `AuthorId`:\n\n1. Make an `opaque type` alias `AuthorId = Long` in a separate file\n   1. Optionally add a subtype constraint `\u003c: Long` if you want to be able to make `AuthorId` type a subtype of `Long` (in other words, to be able to assign an `AuthorId` value to a `Long` reference)\n2. Create a companion object extending `UnderlyingType as RefinedType` (`Long as AuthorId` in this case)\n3. Implement `undefinedValue` and `validation` members\n\n```scala 3\n// AuthorId.scala\nimport dev.akif.as.*\n\nopaque type AuthorId \u003c: Long = Long\n\n// Parenthesis are required because `as` is used as an infix type, which would otherwise be `as[Long, AuthorId]`\nobject AuthorId extends (Long as AuthorId):\n    override val undefinedValue: Long = 0L\n   \n    override val validation: Validation[Long] =\n        Validation.min(lowerLimit = 0L, inclusive = false)\n```\n\nThis incurs no additional allocation because `AuthorId` is defined as an `opaque type` alias. Within `AuthorId.scala` file, it can be treated as a `Long`. However, in any other file, `AuthorId` and `Long` are completely different types.\n\nLet's create other types too.\n\n```scala 3\n// AuthorName.scala\nimport dev.akif.as.*\n\nopaque type AuthorName \u003c: String = String\n\nobject AuthorName extends (String as AuthorName):\n    override val undefinedValue: String = \"\"\n\n    override val validation: Validation[String] =\n       Validation.all(\n          Validation.make(predicate = !_.isBlank, failureMessage = _ =\u003e \"Value is blank\"),\n          Validation.make(predicate = _.length \u003c= 64, failureMessage = _ =\u003e \"Value has more than 64 characters\")\n       )\n\n// BookId.scala\nimport dev.akif.as.*\n\nopaque type BookId \u003c: Long = Long\n\nobject BookId extends (Long as BookId):\n    override val undefinedValue: Long = 0L\n   \n    override val validation: Validation[Long] =\n        Validation.min(lowerLimit = 0L, inclusive = false)\n\n// BookName.scala\nimport dev.akif.as.*\n\nopaque type AuthorName \u003c: String = String\n\nobject AuthorName extends (String as AuthorName):\n    override val undefinedValue: String = \"\"\n\n    override val validation: Validation[String] =\n        Validation.all(\n            Validation.make(predicate = !_.isBlank, failureMessage = _ =\u003e \"Value is blank\"),\n            Validation.make(predicate = _.length \u003c= 64, failureMessage = _ =\u003e \"Value has more than 64 characters\")\n        )\n```\n\nNow let's change the original example.\n\n```scala 3\ncase class Book(id: AuthorId, name: AuthorName)\n\ncase class Book(id: BookId, authorId: AuthorId, name: BookName)\n```\n\nNow our types are fine-tuned so that they will have correct values, and we are forced to check everything at compile time. We need to use `apply` method, `as` extension method or their variants to create a refined value. Creating a refined type by applying an `A` to an `A as B` returns an `E or B`. In other words, object construction can fail and this is reflected to types. Since we have `or`s in the construction, it's easy to combine them, even in for comprehensions.\n\n```scala 3\nimport dev.akif.as.*\nimport e.scala.*\n\n// Failure({\"name\":\"invalid-data\",\"causes\":[{\"message\":\"Value is less than or equal to 0\"}],\"data\":{\"type\":\"AuthorId\",\"value\":\"-1\"}})\nval authorId1: E or AuthorId = AuthorId(-1L)\n\n// Success(1)\nval authorId2: E or AuthorId = AuthorId(1L)\n\n// Can also be written with the extension method\n// val authorId2: E or AuthorId = 1L.as[AuthorId]\n\n// Success(Author(1, Mehmet Akif Tütüncü))\nval maybeAuthor: E or Author =\n    for\n       id   \u003c- AuthorId(1L)\n       name \u003c- AuthorName(\"Mehmet Akif Tütüncü\")\n    yield\n        Author(id, name)\n```\n\nIn cases where you need to have the refined `AuthorId` directly instead of `E or AuthorId`, you can use:\n\n* `AuthorId.applyOrUndefined(-1L)` (or `-1L.asOrUndefined[AuthorId]`) method that will return you the undefined value when validation fails\n* `AuthorId.unsafe(-1L)` (or `-1L.asUnsafe[AuthorId]`) method that will throw the validation error as an exception, hence the name unsafe\n\n```scala 3\n// Book(0, 0, Type Refinement in Scala)\nval book: Book = Book(\n    BookId.undefined,\n    AuthorId.applyOrEmpty(-1L),\n    BookName.unsafe(\"Type Refinement in Scala\")\n)\n```\n\nAccessing the unrefined value is possible via the conveniently named extension method `value`. If defined as a subtype, one can also directly assign a refined value to a reference of the unrefined type.\n\n```scala\n// 0L\nval bookId: Long = book.id.value\n\n// \"Type Refinement in Scala\"\nval bookName: String = book.name // since BookName alias is defined as a subtype of String\n```\n\n## Development and Testing\n\nas is built with SBT. You can use `clean`, `compile`, `test` tasks for development and testing.\n\n## Releases\n\nas packages are published to Maven Central, and they are versioned according to [semantic versioning](https://semver.org). Release process is managed by [sbt-release](https://github.com/sbt/sbt-release).\n\n## Contributing\n\nAll contributions are welcome, including requests to feature your project utilizing as. Please feel free to send a pull request. Thank you.\n\n## License\n\nas is licensed with [MIT License](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakiftutuncu%2Fas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmakiftutuncu%2Fas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakiftutuncu%2Fas/lists"}