{"id":16964997,"url":"https://github.com/propensive/dissonance","last_synced_at":"2025-04-11T23:02:30.481Z","repository":{"id":80754918,"uuid":"571039988","full_name":"propensive/dissonance","owner":"propensive","description":"An implementation of Myers' diff algorithm for Scala","archived":false,"fork":false,"pushed_at":"2025-01-30T21:24:57.000Z","size":3706,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-06T07:46:35.076Z","etag":null,"topics":["diff","myers-algorithm","scala"],"latest_commit_sha":null,"homepage":"https://soundness.dev/dissonance/","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":"2022-11-27T00:26:46.000Z","updated_at":"2025-01-30T21:25:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"f4a180d1-1a45-49ec-890d-050657f85baf","html_url":"https://github.com/propensive/dissonance","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fdissonance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fdissonance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fdissonance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fdissonance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/dissonance/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248492876,"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":["diff","myers-algorithm","scala"],"created_at":"2024-10-13T23:44:48.287Z","updated_at":"2025-04-11T23:02:30.450Z","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/dissonance/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/dissonance/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# Dissonance\n\n__Myers' diff algorithm in Scala__\n\n__Dissonance__ implements Eugene Myers' diff algorithm in Scala as a pure function\non immutable data structures. Using it is as simple as calling\n`diff(left, right)`, where `left` and `right` are sequences of like-typed data\nto be compared; the result is an instance of `Diff`, a sequence of additions,\ndeletions and no-change nodes representing each item in the left and right\nsequence.\n\n## Features\n\n- implements Myers' diff algorithm\n- can be used with any data type, not just strings\n- specify a custom comparison function enabling fine-grained merges on \"similar\" data\n- diffs are simple immutable structures of `Del`, `Ins` and `Par` nodes\n\n\n## Availability\n\nDissonance is available as a binary for Scala 3.4.0 and later, from [Maven\nCentral](https://central.sonatype.com). To include it in an `sbt` build, use\nthe coordinates:\n```scala\nlibraryDependencies += \"dev.soundness\" % \"dissonance-core\" % \"0.3.0\"\n```\n\n\n\n\n\n\n\n## Getting Started\n\nDissonance provides only a single method, `diff`, which takes a `left` and\n`right` parameter, both sequences of the same type, and generates a sequence of\nthe edits required to transform the left sequence into the right sequence, a\nresult of type `Diff`. Each element of the resultant sequence is either a `Par`\nvalue (for _parity_), corresponding to a value in both the left and right\nsequences, an `Ins` value (for _insertions_) which exists only in the right\nsequence, or a `Del` value (for _deletions_) which exists only in the left\nsequence.\n\nThe naming of these enumeration cases corresponds to a translation of the left\nsequence into the right sequence, but could describe a translation from the\nright sequence to the left if the roles are reversed. The `Diff#flip` method\ncan automatically reverse the translation.\n\nEach of the three possible `Edit` cases, `Ins`, `Del` and `Par` includes the\nrelevant value, as well as the indices of that value in each sequence it exists\nin: for `Ins`, the right; for `Del`, the left, and for `Par` both a `left` and\n`right` index.\n\n### Custom equality\n\nBy default, elements of the left and right sequences will be considered _the\nsame_ (producing `Par` values) if they are equal according to Java's universal\nequality method, `AnyRef#equals`. However, other forms of equality (or\nsimilarity) may exist, and it may be useful to consider two elements to be _the\nsame_, even if they are not equal according to `AnyRef#equals`. A common example\nwould be if they had the same ID, even if their content is different.\n\nThe `diff` method takes an optional third parameter, `compare`, of type\n`(ElemType, ElemType) -\u003e Boolean` which determines whether two elements are\n_the same_ for the purposes of the diff.\n\n### Collation\n\nGiven a `Diff` instance, created from two sequences, its `collate` method can be\nused to group subsequences of changes together into changed and unchanged\nregions. For any given _diff_ result, this collation can be done unambiguously:\nruns of adjacent `Par`s are grouped together into an `Unchanged` instance, and\nbetween each run will be one or more `Ins` and/or `Del` values which are grouped\ninto a `Changed` instance, consisting of a sequence of deletions and a sequence of\ninsertions.\n\nConventionally, the diff algorithm will output deletions before insertions, but in\nterms of correctness, the order in which deletions and insertions are applied does\nnot matter. The result of collation will always be an alternating sequence of\n`Changed` and `Unchanged` sections.\n\n### Deeper Diff\n\n`Diff` also provides a method, `rdiff`, which will use these collated changes and\nperform a further diff on each changed section which contains at least one insertion\nand at least one deletion, since some of these may be more usefully viewed as\n_substitutions_ or replacements, rather than unrelated insertions and deletions. The\n`rdiff` method requires a _similarity_ function, `(ElemType, ElemType) -\u003e Boolean`,\nwhich can identify values in the left and right sequences which are similar (but\nwhich have already been identified as non-equal). These are thus presented as `Sub`\nnodes.\n\nSince the order in which each deletion or insertion is applied within a changed\nsection does not affect correctness, each nested diff may decide to identify certain\ninsertion/deletion pairs as \"substitutes\" and fit the remaining insertions and\ndeletions around them.\n\nThe result is an instance of `RDiff` containing a (now flattened) sequence of\n`Ins`s, `Del`s, `Par`s and `Sub`s. Note that all but `Sub` have the supertype,\n`Edit`, while all have the supertype, `Change`.\n\nFurthermore, `rdiff` has a second, optional, parameter, `subSize`, which may be used\nto automatically convert short, changed sections with an equal number of insertions\nand deletions, into a sequence of `Sub`s, provided they are less than `subSize`. The\nvalue defualts to `1`. So, as isolated insertion/deletion region of length `subSize`\nwould become a series of `Sub`s, regardless of whether the similarity function returns\n`true` or `false`.\n\n### Applying a Diff\n\nA `Diff` instance, say `diff`,  may be applied to a sequence, `seq`, with,\n```scala\ndiff.patch(seq)\n```\nto produce a new sequence. This is performed lazily, so the result is a `LazyList`.\n\nBy default, a `Par` \"edit\" will leave the element in the original sequence unchanged.\nHowever, `patch` has an optional second parameter, `update`, which can be provided\nto specify how `Par` edits should be handled. Of course, if the edit is a `Par`, the\noriginal and diff versions of element _should_ be equal anyway, but may not be if,\nfor example, the diff was constructed with a different \"compare\" function or the\ndiff is being incorrectly applied to the wrong input.\n\nThree reasonable alternatives for `update` are:\n- to ignore the value in the diff, and keep the original (the default),\n- to ignore the original value and replace it with the value in the diff\n- to check that the original and diff elements are equal, and fail if they are not\n\nBut more involved possibilities are available which could \"merge\" the original and\ndiff elements, or prefer one or the other.\n\n\n\n\n\n\n## Status\n\nDissonance 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\nDissonance is designed to be _small_. Its entire source code currently consists\nof 358 lines of code.\n\n## Building\n\nDissonance 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 Dissonance?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Dissonance'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 Dissonance 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 `dissonance`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Dissonance'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 Dissonance are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/dissonance/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Dissonance\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\nDissonance 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_Dissonance_'s purpose is to detect differences—or dissonance, the places where they are not in agreement—between different objects.\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 sets of concentric circles, offset from each other, and creating an interference pattern, alluding to dissonance between them.\n\n## License\n\nDissonance 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%2Fdissonance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Fdissonance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fdissonance/lists"}