Skip to content

Commit

Permalink
Merge pull request #33 from JD557/meta-ref
Browse files Browse the repository at this point in the history
Add withRefs/asRefs for case classes
  • Loading branch information
JD557 authored Aug 1, 2023
2 parents 6e2ad3c + 6332047 commit 440b83a
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
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"))

0 comments on commit 440b83a

Please sign in to comment.