https://github.com/andyglow/scala-patch
Scala Structured Patch
https://github.com/andyglow/scala-patch
Last synced: about 1 year ago
JSON representation
Scala Structured Patch
- Host: GitHub
- URL: https://github.com/andyglow/scala-patch
- Owner: andyglow
- Created: 2020-05-29T22:31:49.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2022-08-26T17:28:22.000Z (almost 4 years ago)
- Last Synced: 2025-03-29T18:36:29.105Z (about 1 year ago)
- Language: Scala
- Size: 140 KB
- Stars: 5
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Scala GPL
> GPL: Generic Programming Library
The library aims to provide several concepts missed in vanilla scala:
- (make) enhanced approach to create new instances of case classes
- (patch) ability to compare, create patch, apply patch for standard scala types
[](https://cloud.drone.io/andyglow/scala-patch)


Supported types:
- basic types like `string`, `boolean`, `numeric`
- temporal types (`java.time`, `java.util`, `java.sql`)
- collections
- `unordered` (Seq, Iterable, ...)
- `ordered` (LinerSeq, List, LazyList, ...)
- `indexed` (Array, Vector, ...)
- `keyed` (Maps)
- generic sum types (Option, Either)
- product types (case classes)
## Example
```scala
import scalax.patch._
// values
val left = List(1, 2, 3)
val right = List(1, 3, 4, 5)
// make a patch
val patch = Patch.make(left, right)
// apply a patch
patch(left) == right
// apply inverted patch
patch.inverted(right) == left
// patch is structured
// toString
println(patch)
patch> UpdateOrdered(Diff(Upgrade(IncreaseValue(1),IncreaseValue(1)),Insert(4)))
// patch rendered
PatchVisitor stringify patch
patch> upgrade
patch> - increase 1
patch> - increase 1
patch> insert
patch> - 4
```
## Introduction
Often we need more specific explanation of why one value is not equal to another or, more specifically,
what is a delta of two values, or
what should be modified in value `A` (and how) so it become equal to value `B`.
When we may need it?
- Testing. More detailed difference reports.
- In network enabled applications where update remote state (patch size is in average less in size then an updated state copy)
- In CQRS applications it might be helpful to disassemble a diff of 2 states into a sequence of events.
## Design
### Algebra
The Patch is a sum type of following definition
```
Patch[T] =
UpdateValue (from: T, to: T) |
SetValue (to: T) |
UnsetValue (from: T) |
IncreaseValue (delta: ArithmeticAdapter[T]#Delta) |
DecreaseValue (delta: ArithmeticAdapter[T]#Delta) |
UpdateIndexed [F[_], V] (delta: Map[Int, Patch[V]], sizeDelta: Int) where T = F[V] |
UpdateKeyed [F[_, _], K, V] (delta: Map[K, Patch[T]]) where T = F[K, V] |
UpdateUnordered [F[_], V] (delta: UnorderedAdapter[F, T]#Diff) where T = F[V] and
UnorderedAdapter.Diff[T] = Seq[UnorderedAdapter.Diff.Evt[T]] where
UnorderedAdapter.Diff.Evt[T] =
Add(items: Seq[T]) |
Remove(items: Seq[T]) |
UpdateOrdered [F[_], V] (delta: OrderedAdapter[F, T]#Diff)
OrderedAdapter.Diff[T] = List[OrderedAdapter.Diff.Evt[T]] where
OrderedAdapter.Diff.Evt[T] =
Skip(n: Int) |
Insert(items: List[T]) |
Drop(items: List[T]) |
Update(patches: List[Patch[T]])
```
## Derivation
Patch Maker derivation is provided for case classes.
```scala
// case classes
case class CC(
name: String,
age: Int,
props: Map[String, String])
object CC {
implicit val ccPM: PatchMaker[CC] = DerivePatchMaker.derive[CC]
}
val left = CC("shelly", 23, Map("prop1" -> "v1", "prop2" -> "v2"))
val right = CC("cristine", 37, Map("prop1" -> "vv1", "prop2" -> "vv2"))
val patch = Patch.make(left, right)
println(patch)
patch> $CC$Patch(UpdateValue(shelly,cristine),IncreaseValue(14),UpdateKeyed(Map(prop1 -> UpdateValue(v1,vv1), prop2 -> UpdateValue(v2,vv2))))
PatchVisitor stringify patch
patch> field 'name' {
patch> update: from shelly
patch> : to cristine
patch> }
patch> field 'age' {
patch> increase 14
patch> }
patch> field 'props' {
patch> key 'prop1' {
patch> update: from v1
patch> : to vv1
patch> }
patch> key 'prop2' {
patch> update: from v2
patch> : to vv2
patch> }
patch> }
```
## Text
By default string patch gives you a Constant patch (SetValue, UpdateValue, UnsetValue),
which under some circumstances may look non optimal.
For more sophisticated string manipulation you can use `texts` module`
```scala
libraryDependencies += "com.github.andyglow" %% "scala-patch-texts" % $version
```
It is based on google's `patch-match-diff` library and give more detailed patches over strings.
All you need is
```scala
import scalax.patch.texts._
```
Example:
```scala
import scalax.patch.texts._
val patch = Patch.make("hello, dear friend!", "hello, my friend!")
patch> TextPatch(Step(3, 3, 12, 10, Equal("lo, "), Delete("dear"), Insert("my"), Equal(" fri")))
```