{"id":16964953,"url":"https://github.com/propensive/rudiments","last_synced_at":"2025-03-22T14:30:59.681Z","repository":{"id":37234043,"uuid":"233590303","full_name":"propensive/rudiments","owner":"propensive","description":"Rudimentary methods for elegant, everyday Scala","archived":false,"fork":false,"pushed_at":"2025-02-12T06:22:36.000Z","size":2506,"stargazers_count":9,"open_issues_count":4,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-18T11:52:00.254Z","etag":null,"topics":["control-flow","predef","scala","scala-predef","y-combinator"],"latest_commit_sha":null,"homepage":"https://soundness.dev/rudiments/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/propensive.png","metadata":{"files":{"readme":".github/readme.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":null,"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-01-13T12:27:27.000Z","updated_at":"2025-02-12T06:22:39.000Z","dependencies_parsed_at":"2023-02-19T17:00:26.326Z","dependency_job_id":"d1b8365f-87ac-4560-bf5a-79d7e9a27d75","html_url":"https://github.com/propensive/rudiments","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Frudiments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Frudiments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Frudiments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Frudiments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/rudiments/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244971769,"owners_count":20540852,"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":["control-flow","predef","scala","scala-predef","y-combinator"],"created_at":"2024-10-13T23:44:40.960Z","updated_at":"2025-03-22T14:30:59.666Z","avatar_url":"https://github.com/propensive.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[\u003cimg alt=\"GitHub Workflow\" src=\"https://img.shields.io/github/actions/workflow/status/propensive/rudiments/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/rudiments/actions)\n[\u003cimg src=\"https://img.shields.io/discord/633198088311537684?color=8899f7\u0026label=DISCORD\u0026style=for-the-badge\" height=\"24\"\u003e](https://discord.com/invite/MBUrkTgMnA)\n\u003cimg src=\"/doc/images/github.png\" valign=\"middle\"\u003e\n\n# Rudiments\n\n__Rudimentary methods for elegant, everyday Scala__\n\n_Rudiments_ provides a small collection of tiny but useful utilities for everyday programming in Scala, and\ncould be considered an enhanced \"predef\".\n\n## Features\n\n- implementation of an easier-to-use Y-combinator method, `fix` with a recursion helper method, `recur`\n- typesafe and mutation-safe reimplementations of several `String` operations\n- `String` extractors for primitive types\n- `unit`, `only`, `twin` and `triple` convenience methods to make some code patterns slightly more concise\n- a typesafe `str\"\"` string interpolator where all substitutions must be `String`s\n\n\n## Availability\n\nRudiments has not yet been published. The medium-term plan is to build it with\n[Fury](https://github.com/propensive/fury) and to publish it as a source build\non [Vent](https://github.com/propensive/vent). This will enable ordinary users\nto write and build software which depends on Rudiments.\n\nSubsequently, Rudiments will also be made available as a binary in the Maven\nCentral repository. This will enable users of other build tools to use it.\n\nFor the overeager, curious and impatient, see [building](#building).\n\n\n\n\n\n\n## Getting Started\n\nUtilities in _Rudiments_ are mostly provided through extension methods, and importing the `rudiments` package\nwill bring all utility methods into scope.\n\n### Y-Combinator\n\nAn implementation of a [Y-Combinator](https://shorturl.at/jqKOY), called `fix`, is provided, implemented using a\nScala 3 context function, which enables slightly more favorable syntax than was possible in Scala 2. This method\nmakes it easier to write code in a point-free style.\n\nThe `fix` method takes a type parameter (which must be explicitly specified for type inference) and a lambda as\nits first parameter, to which an additional parameter should be supplied as its initial value. Crucially, in the\nbody of `fix`'s lambda, a call to `recur` should be used to signal recursion.\n\nThis is best illustrated with an example. Here is an implementation of a factorial function.\n```scala\ndef factorial(n: Int): Int =\n  fix[Int] { i =\u003e if i \u003c= 0 then 1 else i*recur(i - 1) } (n)\n```\n\nThis avoids the explicit definition of a private or nested helper function, which would normally be necessary\nfor a definition such as this.\n\n### Primitive `String` Extractors\n\nExtractors for all the primitive types are provided for matching on `String`s. These are defined as extensions\nto the (virtual) companion objects of the primitive types, so they have the same names as the types they\nextract.\n\nHere is an example of them in use:\n```scala\ndef parse(number: String): Boolean | Int | Double =\n  number match\n    case Boolean(b) =\u003e b\n    case Int(i)     =\u003e i\n    case Double(d)  =\u003e d\n    case _          =\u003e 0\n```\n\n### Typesafe `String` operations\n\nThe extension method `String#cut` has identical semantics to `String#split`, but returns an immutable `IArray`\ninstead of an `Array`. Likewise, the methods `String#bytes` and `String#chars` mirror `String#getBytes` and\n`String#toCharArray`, returning an `IArray[Byte]` and `IArray[Char]` respectively.\n\nThe `bytes` method currently uses the `UTF-8` in all cases, though this may change if there is sufficient\ndemand.\n\nFour variants of the extension method `join` are provided on `Traversable[String]` instances, which provide the\nsame functionality as `mkString` (but only operating on `String`s) with one additional two-parameter version\nthat's specialized for natural language lists where each element is separated by a comma except the last, which\nis preceded by a word such as `and` or `or`.\n\nFor example,\n```scala\nList(\"one\", \"two\", \"three\").join(\", \", \" or maybe \")\n```\nwill produce the string `one, two or maybe three`.\n\n### Lightweight system property access\n\nMany JVM system properties are available in the map, `System.getProperties` and are typically given identifiers\nwritten in a dot-notation style, such as `user.dir`. Rudiments provides syntactic sugar for accessing these\ndynamically through the `Sys` object, for example,\n```scala\nval pwd: Option[String] = Sys.user.dir()\n```\n\n### `unit`\n\nOften a side-effecting expression returns a value which is not used at a particular call site, and can be\ndiscarded. However, the expression's return type can result in type-inference choosing an undesired return type,\nwhen `Unit` would be preferable, or a compile-time warning about discarded values may be produced.\n\nThe `unit` extension method silently discards the return value of any expression, and instead produces a `Unit`,\n`()`.\n\n### `only`\n\nThe `only` extension method applies a partial function to a value and lifts the result into an option.\n\nFor example,\n```scala\nval result: Option[Int] = string.only { case Int(i) =\u003e i*i }\n```\n\n### `str\"\"` Interpolator\n\nThe `s\"\"` interpolator takes parameters of `Any` type as substitutions, calling `String#toString` on them as\nnecessary. This may be considered too permissive, so `str\"\"` is provided as a typesafe alternative that requires\nevery substitution to be a `String`.\n\n### `twin` and `triple`\n\nThese two extension methods produce a two-tuple and a three-tuple (respectively) of repetitions of the value it\nis applied to. This can be useful in a subsequent `map` operation.\n\n\n\n\n\n## Status\n\nRudiments is classified as __maturescent__. For reference, Soundness projects are\ncategorized into one of the following five stability levels:\n\n- _embryonic_: for experimental or demonstrative purposes only, without any guarantees of longevity\n- _fledgling_: of proven utility, seeking contributions, but liable to significant redesigns\n- _maturescent_: major design decisions broady settled, seeking probatory adoption and refinement\n- _dependable_: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version `1.0.0` or later\n- _adamantine_: proven, reliable and production-ready, with no further breaking changes ever anticipated\n\nProjects at any stability level, even _embryonic_ projects, can still be used,\nas long as caution is taken to avoid a mismatch between the project's stability\nlevel and the required stability and maintainability of your own project.\n\nRudiments is designed to be _small_. Its entire source code currently consists\nof 924 lines of code.\n\n## Building\n\nRudiments will ultimately be built by Fury, when it is published. In the\nmeantime, two possibilities are offered, however they are acknowledged to be\nfragile, inadequately tested, and unsuitable for anything more than\nexperimentation. They are provided only for the necessity of providing _some_\nanswer to the question, \"how can I try Rudiments?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Rudiments's build\n   structure, dependencies and source location; the file format should be short\n   and quite intuitive. Copy the sources into a source directory in your own\n   project, then repeat (recursively) for each of the dependencies.\n\n   The sources are compiled against the latest nightly release of Scala 3.\n   There should be no problem to compile the project together with all of its\n   dependencies in a single compilation.\n\n2. *Build with [Wrath](https://github.com/propensive/wrath/)*\n\n   Wrath is a bootstrapping script for building Rudiments and other projects in\n   the absence of a fully-featured build tool. It is designed to read the `fury`\n   file in the project directory, and produce a collection of JAR files which can\n   be added to a classpath, by compiling the project and all of its dependencies,\n   including the Scala compiler itself.\n   \n   Download the latest version of\n   [`wrath`](https://github.com/propensive/wrath/releases/latest), make it\n   executable, and add it to your path, for example by copying it to\n   `/usr/local/bin/`.\n\n   Clone this repository inside an empty directory, so that the build can\n   safely make clones of repositories it depends on as _peers_ of `rudiments`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Rudiments's dependencies.\n\n   If the build was successful, the compiled JAR files can be found in the\n   `.wrath/dist` directory.\n\n## Contributing\n\nContributors to Rudiments are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/rudiments/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Rudiments\neasier.\n\nPlease __do not__ contact project maintainers privately with questions unless\nthere is a good reason to keep them private. While it can be tempting to\nrepsond to such questions, private answers cannot be shared with a wider\naudience, and it can result in duplication of effort.\n\n## Author\n\nRudiments was designed and developed by Jon Pretty, and commercial support and\ntraining on all aspects of Scala 3 is available from [Propensive\nO\u0026Uuml;](https://propensive.com/).\n\n\n\n## Name\n\nThe word _rudiment_ is defined as, \"the principle which lies at the bottom of any development; an unfinished beginning\", which is apt for a library whose purpose is to provide such common functionality that it might lie at the start of many other libraries.\n\nIn general, Soundness project names are always chosen with some rationale,\nhowever it is usually frivolous. Each name is chosen for more for its\n_uniqueness_ and _intrigue_ than its concision or catchiness, and there is no\nbias towards names with positive or \"nice\" meanings—since many of the libraries\nperform some quite unpleasant tasks.\n\nNames should be English words, though many are obscure or archaic, and it\nshould be noted how willingly English adopts foreign words. Names are generally\nof Greek or Latin origin, and have often arrived in English via a romance\nlanguage.\n\n## Logo\n\nThe logo shows a burning sun, the basis for life in our solar system; it represents the foundational nature of Rudiments.\n\n## License\n\nRudiments is copyright \u0026copy; 2025 Jon Pretty \u0026 Propensive O\u0026Uuml;, and\nis made available under the [Apache 2.0 License](/license.md).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Frudiments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Frudiments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Frudiments/lists"}