{"id":16964975,"url":"https://github.com/propensive/spectacular","last_synced_at":"2025-04-11T23:02:36.772Z","repository":{"id":164081189,"uuid":"639036336","full_name":"propensive/spectacular","owner":"propensive","description":"Scala typeclasses for rendering different values as text for different audiences","archived":false,"fork":false,"pushed_at":"2025-02-11T23:54:12.000Z","size":1431,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-06T07:46:37.459Z","etag":null,"topics":["scala","show","show-typeclass","text-conversion"],"latest_commit_sha":null,"homepage":"https://soundness.dev/spectacular/","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-05-10T16:10:36.000Z","updated_at":"2025-02-11T23:54:15.000Z","dependencies_parsed_at":"2024-03-27T17:29:08.338Z","dependency_job_id":"b0e69bea-17d3-48f9-9dac-c257a23b5a57","html_url":"https://github.com/propensive/spectacular","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fspectacular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fspectacular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fspectacular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fspectacular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/spectacular/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248492874,"owners_count":21113163,"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":["scala","show","show-typeclass","text-conversion"],"created_at":"2024-10-13T23:44:44.437Z","updated_at":"2025-04-11T23:02:36.746Z","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/spectacular/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/spectacular/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# Spectacular\n\n__Typeclasses for rendering different values as text for different audiences__\n\nThe idea of a `Show` typeclass is well established as a better alternative to using `toString`, but it\nis limited by its inability to distinguish different audiences. A fully-generic typeclass for\narbitrary different audiences is possible, but is usually better handled with different `Text` types\ntoo. Spectacular's compromise of distinguishing with `Show` and `Debug` typeclasses will be sufficient\nfor most purposes, and has the distinction that `debug` will always provide _some_ `Text` value, while\n`show` will require an appropriate `Show` instance to be provided.\n\n## Features\n\n- typeclass-based means of converting values to text\n- defines the `Show` typeclass for text destined for end users\n- defines the `Debug` typeclass for text destined for the developer\n- extension methods `show` and `debug` will apply conversions using these typeclasses\n- the `show` method may fail if a `Show` is not available\n- the `debug` method will fall back to a `Show` method and then `toString` if no `Debug` instance exists\n- `Debug` instances are generically derived for products and coproducts\n\n\n## Availability\n\n\n\n\n\n\n\n## Getting Started\n\nAll Spectacular terms and types are defined in the `spectacular` package:\n```scala\nimport spectacular.*\n```\n\n### `Show` and `Debug`\n\nGiven a value of some type, e.g. a value `artifact` of type `Artifact`, calling\n`artifact.show` will try to resolve a `Show[Artifact]` typeclass and apply it\nto `artifact`. If such a contextual instance exists in scope the result will be\na `Text` value; if it does not, then a compile error will be raised.\n\nCalling `artifact.debug` works similarly, but will never fail to compile:\nFirst, a `Debug[Artifact]` will be sought and applied. Failing that an\n`Encoder[Artifact]` will be used (see below). Then, a `Show[Artifact]` will be\ntried instead. Finally, if that fails, a `Text` value will be created from\n`artifact.toString`. Since `toString` is always defined (however inadequately),\nthe call always succeeds.\n\nNote that this only applies when calling `debug` on a value; summoning the\n`Debug` instance and applying it directly will still fail to compile if no\n`Debug` instance exists in scope.\n\nBoth `Show` and `Debug` provide very similar functionality, and for many types,\ntheir implementations will be the same. They differ primarily in their intended\naudience: `Show` instances should produce textual output which is intended for\n_users_, not _programmers_, while `Debug` output is intended only for\nprogrammers. The absence of a `Show` typeclass for a particular type\ncorresponds to that type not being intended for user consumption, while a\nprogrammer should always be happy to see _some representation_ of a value,\nhowever technical.\n\nThus, messages which are intended for end-users should use `Show` and\n\"internal\" messages should use `Debug`.\n\n\n### Defining `Show` and `Debug` instances\n\nCreating a given instance of `Show` or `Debug` is simple in both cases. Both\nare single-abstract-method traits, so definitions are often take just a single\nline. For example,\n```scala\ngiven Debug[Short] = short =\u003e t\"Short(${short.toString})\"\n```\n\nSince the `debug` extension method will fall back to using a `Show` instance\nwhenever a `Debug` instance is not available, if the implementations are\nidentical, then it is sufficient just to provide a `Show` instance.\n\n### Generic derivation\n\nEvery product type, such as case classes, will have a default `Debug` instance,\nderived from calling `debug` on each of its parameters. This will exist even if\nsome parameters do not have their own `Debug` instance, since a `Show`\ntypeclass will be used as a fallback, and `toString` as a last resort.\n\nFor example, the case class,\n```scala\nimport anticipation.Text\n\nclass Id(id: String) { override def toString(): String = id }\ncase class Person(id: Id, name: Text, age: Int)\nval person = Person(Id(\"jqm\"), t\"Jennifer\", 61).debug\n```\nwill produce the text,\n```\nPerson(id=jqm,name=t\"Jennifer\",age=61)\n```\nusing the `toString` of `Id` and the `Debug` instances (provided by\nSpectacular) for `Text` and `age`.  Note that `Text`'s `Debug` instance\nproduces pastable code, rather than simply returning the exact same `Text`\nvalue, while its `Show` instance does exactly that.\n\n### Showing `Boolean` values\n\nThe values `true` and `false` often mean different things in different\nscenarios, and without specifying, Spectacular will not presume any particular\n`show` serialization of a `Boolean` value.\n\nBut by importing a `BooleanStyle` value from the `spectacular.booleanStyles`\npackage, a style can be chosen, for example,\n```scala\nimport turbulence.Out\nimport turbulence.stdioSources.jvm\nimport booleanStyles.yesNo\n\ndef run(): Unit = Out.println(true.show)\n```\nwill print the text `yes`.\n\nSeveral `BooleanStyle` options are provided in the `booleanStyles` package,\n - `yesNo` - `yes` or `no`\n - `onOff` - `on` or `off`\n - `trueFalse` - `true` or `false`\n - `oneZero` - `1` (`true`) or `0` (`false`)\nand it is trivial to provide alternatives, for example:\n```scala\nimport gossamer.*\ngiven posNeg: BooleanStyle = BooleanStyle(t\"+\", t\"-\")\n```\n\n### `Encoder` and `Decoder`\n\nA further typeclass, `Encoder`, also converts from a particular type to `Text`,\nbut comes with a complementary `Decoder` typeclass and has a particular intent:\nit is intended to represent a canonical way to encode a value as a string, such\nthat that text may be decoded to restore the original value.\n\nFor example, a `Url` (as defined in\n[Nettlesome](https://github.com/propensive/nettlesome/)) represents the\nstructure of a URL, but is encoded in a very standard way to a familiar\nrepresentation of a URL, such as `https://example.com/`. This conversion should\nbe provided by an `Encoder` instance, and a corresponding `Decoder` should be\nprovided in order to parse the `Text` representation of the URL back into a\n`Url` instance.\n\n`Encoder`s and `Decoder`s are intended to be used by libraries which use text\nas a serialization format. [Jacinta](https://github.com/propensive/jacinta/)\nallows any type for which an `Encoder` exists to be serialized to JSON, and any\ntype for which a `Decoder` exists to be read from JSON.\n[Xylophone](https://github.com/propensive/xylophone/) provides the same\nfunctionality for XML and [Cellulose](https://github.com/propensive/cellulose/)\nfor CoDL.\n\n#### Decoding errors\n\nWhile encoding to text will normally succeed in all cases, it's common for\ndecoder (or deserialization) to fail, if the input text is in the wrong format.\nHowever, the API of `Decoder` does not include any optionality in the signature\nof its `decode` method. That's because _capabilities_ should be used to handle\nfailures, with greater flexibility.  Given `Decoder` instances should include\nappropriate `using` clauses to demand the capability to raise errors. If using\n[Contingency](https://github.com/propensive/contingency/) for error handling, that\nimplies a `Raises` instance, while Scala's checked exceptions require a\n`CanThrow` instance for the exception type.\n\n\n\n\n\n## Status\n\nSpectacular 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\nSpectacular is designed to be _small_. Its entire source code currently consists\nof 367 lines of code.\n\n## Building\n\nSpectacular 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 Spectacular?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Spectacular'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 Spectacular 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 `spectacular`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Spectacular'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 Spectacular are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/spectacular/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Spectacular\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\nSpectacular 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\nSomething _spectacular_ relates to a _spectacle_ or _show_; in this case, the `Show` typeclass.\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 is a film reel, the medium for distributing a _show_.\n\n## License\n\nSpectacular 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%2Fspectacular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Fspectacular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fspectacular/lists"}