{"id":16964985,"url":"https://github.com/propensive/symbolism","last_synced_at":"2025-10-09T13:35:56.019Z","repository":{"id":183036606,"uuid":"669508475","full_name":"propensive/symbolism","owner":"propensive","description":"A general mechanism to implement symbolic operators in Scala","archived":false,"fork":false,"pushed_at":"2025-01-25T18:54:31.000Z","size":743,"stargazers_count":6,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-05T01:43:06.401Z","etag":null,"topics":["arithmetic-operators","scala","typeclasses"],"latest_commit_sha":null,"homepage":"https://soundness.dev/symbolism/","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,"zenodo":null}},"created_at":"2023-07-22T14:03:19.000Z","updated_at":"2025-01-25T18:54:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"4e9f88ee-3915-4a97-8b47-29bc05773c1d","html_url":"https://github.com/propensive/symbolism","commit_stats":null,"previous_names":["propensive/symbolism"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/propensive/symbolism","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fsymbolism","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fsymbolism/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fsymbolism/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fsymbolism/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/symbolism/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fsymbolism/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001479,"owners_count":26083102,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["arithmetic-operators","scala","typeclasses"],"created_at":"2024-10-13T23:44:46.616Z","updated_at":"2025-10-09T13:35:55.987Z","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/symbolism/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/symbolism/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# Symbolism\n\n__A general mechanism to implement overloaded symbolic operators__\n\nMany different types support, in some way, the concept of arithmetic\noperations: they can be added, subtracted, multiplied and divided. Or perhaps\njust some of these. We want to use the familiar binary arithmetic operators\n(`+`, `-`, `*` and `/`) to work with these types, so that is the first thing\n__Symbolism__ provides, though typeclass definitions for the four main arithmetic\noperations, plus negation and square and cube root methods.\n\n## Features\n\n- uses typeclasses as a modular way to implement symbolic operators\n- avoids overloading errors when mixing different projects which define symbolic extension methods\n- provides typeclasses for the arithmetic operators, `+`, `-`, `*` and `/`\n- provides a single inequality typeclass for the comparison operators, `\u003c`, `\u003c=`, `\u003e` and `\u003e=`\n\n\n## Availability\n\nSymbolism 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 Symbolism.\n\n\n\n\n\n\n## Getting Started\n\n\nThese work out-of-the-box: if your type provides an appropriate arithmetic typeclass\ninstance, you can use its arithmetic operator. The typeclasses are:\n- `Addable`\n- `Subtractable`\n- `Multiplicable`\n- `Divisible`\n- `Negatable`, and\n- `Rootable`\n\nBut these typeclasses provide another important facility: they allow us to\nabstract over the arithmetic operations. That means it's possible to write code\nagainst these typeclasses which will work on any type which implements them.\n\nFor example, [Mosquito](https://github.com/propensive/mosquito/) is a library\nfor working with Euclidean vectors (called `Euclidean`) and matrices of\narbitrary types, such as `Double`s or even `Exception`s. With Mosquito, you can\nadd two vectors if, and only if, you can add their elements. So you can add two\nvectors of `Double` (as long as they have the same dimension), but you can't\nadd two vectors of `Exception` because `Exception`s have no `Addable` instance.\n\nWe can go further, and calculate the scalar (dot) product if we also have a\n`Multiplicable` typeclass, and the vector (cross) product if we have a\n`Subtractable` typeclass too.\n\nSimilarly, [Baroque](https://github.com/propensive/baroque/) provides an\nimplementation of complex numbers, which are generic in the common type used\nfor their real and imaginary parts. Operations on Baroque complex numbers are\nprovided if their generic types support the necessary operations.\n\n[Quantitative](https://github.com/propensive/quantitative/) offers\nrepresentations of physical quantities, such as _10Nm_ (10 Newton Metres), and\nsupports all arithmetic operations on these values through Symbolism typeclass\ninstances. The types of the operands do not need to be the same, and the units\nof the result type can be computed from the input units. For example, _10Nm /\n5s_ would produce a value of _2Nm/s_. This is no problem at all for Symbolism.\nNor is it problematic for Mosquito which would be happy to calculate the cross\nproduct of two vectors of different units, and compute the resultant units.\n\nThe power of this abstraction is demonstrated by Mosquito, Baroque and\nQuantitative all having a dependency on Symbolism, but no dependencies on each\nother.\n\n### Defining Binary Arithmetic Typeclasses\n\nSymbolism's binary arithmetic typeclasses are determined by three types\nrepresenting the left operand, the right operand and the result type. Often\nthese will all be the same type, but it's possible for them to differ, for example\nwhen multiplying a `String` by an `Int`, with a `String` as the result.\n\nThe type of each typeclass can be specified is the form,\n`LeftOperand is Operation[RightOperand] into Result`. Hence, we have:\n- `Augend is Addable by Addend into Result`\n- `Minuend is Subtractable by Subtrahend into Result`\n- `Multiplicand is Multiplicable by Multiplier into Result`\n- `Dividend is Divisible by Divisor into Result`\nand have the methods, `add`, `subtract`, `multiply` and `divide` defined upon them.\n\nImplementing a `given` instance is as simple as defining. For example, imagine\nadding a length of time (a `Duration`) to a point in time (an `Instant`). We\ncould write,\n```scala\ngiven Instant is Addable by Duration into Instant:\n  def add(left: Instant, right: Duration): Instant = left.plus(right)\n```\nor even,\n```scala\ngiven Instant is Addable by Duration into Instant = _.plus(_)\n```\ntaking advantage of the fact that `Addable` is a SAM type.\n\nIf we were defining our own `Instant` and `Duration` types, we might write this:\n```scala\nobject Duration:\n  given Duration is Addable by Duration into Duration = _.plus(_)\n  given Duration is Subtractable by Duration into Duration = _.minus(_)\n  given Duration is Multiplicable by Double into Duration = _.times(_)\n  given Duration is Divisible by Double into Dulation = _.divide(_)\n\nobject Instant:\n  given Instant is Addable by Duration into Instant = _.plus(_)\n  given Instant is Subtractable by Instant into Duration = _.minus(_)\n  given Instant is Subtractable by Duration into Instant = _.to(_)\n```\n\nThe typeclasses are defined in the companion object of the type of their left\noperand, because this is how they will be resolved; not by the right operand.\nBut notice how it's possible to define two different `Subtractable` instances\non `Instant`. The correct typeclass will be chosen depending on the type of the\nright operand.\n\n### Writing Generic Code\n\nImagine, instead of providing new types with arithmetic operations, we want to\nwrite a generic method which can work with any such type. We should demand the\nappropriate typeclass instance in the method's signature, as a `using`\nparameter.\n\nHere's what that might look like for a method that needs to multiply two values\nof the same type:\n```scala\ndef op[Operand, Result](lefts: List[Operand], right: List[Operand])\n    (using Operand is Multiplicable by Operand into Result)\n        : List[Result] =\n  lefts.zip(rights).map(_*_)\n```\n\nWe do not need to name the using parameter, as the `*` operator will resolve it\nautomatically.\n\n### Note on Types\n\nThe nature of these types, composed using the infix type constructors, `is`,\n`by` and `into`, is that any part may be omitted when writing the type. For\nexample, `Instant is Addable` is a type, but it's a type which leaves its right\noperand and result type unspecified.\n\nEqually, `Multiplicable by Int into String` is a type which leaves the left\noperand's type unspecified.\n\nOmitting part of the type can be useful, usally with the result type. If we\nhave an instance of an underspecified type, such as `String is Multiplicable by\nInt`, say `mul`, then we can access its `Result` type as a member of `mul`,\nwith `mul.Result`.\n\nThis comes into its own in method signatures of methods which chain together\nseveral arithmetic operations. Here's an example which multiplies two pairs of\nnumbers, all potentially different types, then subtracts one product from the\nother:\n```scala\ndef op2[A, B, C, D](a: A, b: B, c: C, d: D)\n    (using product1: A is Multiplicable by B,\n           product2: C is Multiplicable by D,\n           sum:      product1.Result is Subtractable by product2.Result)\n        : sum.Result\n  a*b - c*d\n```\n\n### Other Operations\n\nTwo other operations are provided: negation and rooting.\n\n#### Negation\n\nNegation is a unary operator, provided by the `Negatable` typeclass. We can\ndefine a `Negatable` without a `by` clause in its type, for example,\n```scala\ngiven PosInt is Negatable into NegInt = _.negate()\n```\n\n#### Rooting\n\nTBC\n\n\n## Status\n\nSymbolism is classified as __embryotic__. 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\nSymbolism is designed to be _small_. Its entire source code currently consists\nof 185 lines of code.\n\n## Building\n\nSymbolism 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 Symbolism?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Symbolism'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 Symbolism 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 `symbolism`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Symbolism'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 Symbolism are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/symbolism/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Symbolism\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\nSymbolism 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\n__Symbolism__ helps work with _symbolic_ operators\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 three of the four arithmetic operators overlaid upon each other.\n\n## License\n\nSymbolism 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%2Fsymbolism","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Fsymbolism","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fsymbolism/lists"}