Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add withRefs/asRefs for case classes #33

Merged
merged 2 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions core/src/main/scala/eu/joaocosta/interim/api/Ref.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package eu.joaocosta.interim.api

import scala.deriving.Mirror

/** A mutable reference to a variable.
*
* When a function receives a Ref as an argument, it will probably mutate it.
Expand Down Expand Up @@ -47,8 +49,32 @@ object Ref:
block(ref)
ref.value

/** Destructures an object into a tuple of Refs that can be used inside the block.
* In the end, a new object is returned with the updated values
*
* Useful to set temporary mutable variables.
*/
def withRefs[T <: Product](initialValue: T)(using mirror: Mirror.ProductOf[T])(
block: Tuple.Map[mirror.MirroredElemTypes, Ref] => Unit
): T =
val tuple: mirror.MirroredElemTypes = Tuple.fromProductTyped(initialValue)
val refTuple: Tuple.Map[tuple.type, Ref] = tuple.map([T] => (x: T) => Ref(x))
block(refTuple.asInstanceOf)
type UnRef[T] = T match { case Ref[a] => a }
val updatedTuple: mirror.MirroredElemTypes =
refTuple.map([T] => (x: T) => x.asInstanceOf[Ref[_]].value.asInstanceOf[UnRef[T]]).asInstanceOf
mirror.fromTuple(updatedTuple)

/** Wraps this value into a Ref and passes it to a block, returning the final value of the ref.
*
* Useful to set temporary mutable variables.
*/
extension [T](x: T) def asRef(block: Ref[T] => Unit): T = withRef(x)(block)

/** Destructures this value into multiple Refs and passes it to a block, returning the final value of the ref.
*
* Useful to set temporary mutable variables.
*/
extension [T <: Product](x: T)
def asRefs(using mirror: Mirror.ProductOf[T])(block: Tuple.Map[mirror.MirroredElemTypes, Ref] => Unit): T =
withRefs(x)(block)
19 changes: 19 additions & 0 deletions core/src/test/scala/eu/joaocosta/interim/api/RefSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,28 @@ class RefSpec extends munit.FunSuite:
}
assertEquals(result, 2)

// Braces needed due to https://github.com/scalameta/scalafmt/issues/3597
test("withRefs allows to build a case class from temporary Ref value") {
case class Foo(x: Int, y: String)
val result = Ref.withRefs(Foo(1, "asd")) { (x, y) =>
x := 2
y := "dsa"
}
assertEquals(result, Foo(2, "dsa"))
}

test("asRef allows to use a temporary Ref value"):
import Ref.asRef
val result = 0.asRef { ref =>
Ref.modify[Int](ref, _ + 2)
}
assertEquals(result, 2)

test("asRefs allows to build a case class from temporary Ref value"):
import Ref.asRefs
case class Foo(x: Int, y: String)
val result = Foo(1, "asd").asRefs { (x, y) =>
x := 2
y := "dsa"
}
assertEquals(result, Foo(2, "dsa"))
Loading