Skip to content

Commit

Permalink
WIP Add OpaqueIntSafer
Browse files Browse the repository at this point in the history
And significantly reduce overhead of TotalWrapper by changing
methods to not rely on the witness, thus restoring inlining. Note
that this behavior is quite fragile, since scala happily uses the
witness without warning.  I'm not sure if we really need witnesses
here or if we can use regular type constraints?
  • Loading branch information
isaacl committed Oct 12, 2024
1 parent 72a58bd commit 30ba5ea
Showing 1 changed file with 33 additions and 21 deletions.
54 changes: 33 additions & 21 deletions core/src/main/scala/newtypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,34 @@ object newtypes:
type IntRuntime[A] = SameRuntime[A, Int]
type DoubleRuntime[A] = SameRuntime[A, Double]

trait TotalWrapper[Newtype, Impl](using ev: Newtype =:= Impl):
inline def raw(inline a: Newtype): Impl = a
inline def apply(inline s: Impl): Newtype = s.asInstanceOf[Newtype]
inline def from[M[_]](inline f: M[Impl]): M[Newtype] = f.asInstanceOf[M[Newtype]]
inline def from[M[_], B](using sr: SameRuntime[B, Impl])(inline f: M[B]): M[Newtype] =
class TotalWrapper[Newtype, Impl](using Newtype =:= Impl):
inline final def raw(inline a: Newtype): Impl = a.asInstanceOf[Impl]
inline final def apply(inline s: Impl): Newtype = s.asInstanceOf[Newtype]
inline final def from[M[_]](inline f: M[Impl]): M[Newtype] = f.asInstanceOf[M[Newtype]]
inline final def from[M[_], B](using sr: SameRuntime[B, Impl])(inline f: M[B]): M[Newtype] =
f.asInstanceOf[M[Newtype]]
inline def from[M[_], B](inline other: TotalWrapper[B, Impl])(inline f: M[B]): M[Newtype] =
inline final def from[M[_], B](inline other: TotalWrapper[B, Impl])(inline f: M[B]): M[Newtype] =
f.asInstanceOf[M[Newtype]]
inline def raw[M[_]](inline f: M[Newtype]): M[Impl] = f.asInstanceOf[M[Impl]]
inline final def raw[M[_]](inline f: M[Newtype]): M[Impl] = f.asInstanceOf[M[Impl]]

given SameRuntime[Newtype, Impl] = identity
given SameRuntime[Newtype, Impl] = _.asInstanceOf[Impl]
given SameRuntime[Impl, Newtype] = _.asInstanceOf[Newtype]
given (using Eq[Impl]): Eq[Newtype] = Eq.by(_.value)

extension (a: Newtype)
inline def value: Impl = a
inline def into[X](inline other: TotalWrapper[X, Impl]): X = other.apply(a)
inline def map(inline f: Impl => Impl): Newtype = apply(f(a))
inline def value: Impl = a.asInstanceOf[Impl]
inline def into[X](inline other: TotalWrapper[X, Impl]): X = other.apply(a.asInstanceOf[Impl])
inline def map(inline f: Impl => Impl): Newtype = apply(f(a.asInstanceOf[Impl]))
end TotalWrapper

trait FunctionWrapper[Newtype, Impl](using ev: Newtype =:= Impl) extends TotalWrapper[Newtype, Impl]:
extension (a: Newtype) inline def apply: Impl = a
class FunctionWrapper[Newtype, Impl](using Newtype =:= Impl) extends TotalWrapper[Newtype, Impl]:
extension (a: Newtype) inline def apply: Impl = a.asInstanceOf[Impl]

trait OpaqueString[A](using A =:= String) extends TotalWrapper[A, String]:
class OpaqueString[A](using A =:= String) extends TotalWrapper[A, String]:
given Show[A] = _.value
given Render[A] = _.value

trait OpaqueInt[A](using A =:= Int) extends TotalWrapper[A, Int]:
class OpaqueInt[A](using A =:= Int) extends TotalWrapper[A, Int]:
extension (inline a: A)
inline def unary_- = apply(-raw(a))
inline infix def >(inline o: Int): Boolean = raw(a) > o
Expand All @@ -59,8 +59,8 @@ object newtypes:
inline infix def <=(inline o: Int): Boolean = raw(a) <= o
inline infix def +(inline o: Int): A = apply(raw(a) + o)
inline infix def -(inline o: Int): A = apply(raw(a) - o)
inline def atLeast(inline bot: Int): A = apply(math.max(raw(a), bot))
inline def atMost(inline top: Int): A = apply(math.min(raw(a), top))
inline def atLeast(inline bot: Int): A = apply(Math.max(raw(a), bot))
inline def atMost(inline top: Int): A = apply(Math.min(raw(a), top))
inline infix def >(inline o: A): Boolean = >(raw(o))
inline infix def <(inline o: A): Boolean = <(raw(o))
inline infix def >=(inline o: A): Boolean = >=(raw(o))
Expand All @@ -70,13 +70,25 @@ object newtypes:
inline def atLeast(inline bot: A): A = atLeast(raw(bot))
inline def atMost(inline top: A): A = atMost(raw(top))

trait OpaqueLong[A](using A =:= Long) extends TotalWrapper[A, Long]
trait OpaqueDouble[A](using A =:= Double) extends TotalWrapper[A, Double]:
class OpaqueIntSafer[A](using A =:= Int) extends TotalWrapper[A, Int]:
extension (inline a: A)
inline def unary_- = apply(-raw(a))
inline infix def >(inline o: A): Boolean = raw(a) > raw(o)
inline infix def <(inline o: A): Boolean = raw(a) < raw(o)
inline infix def >=(inline o: A): Boolean = raw(a) >= raw(o)
inline infix def <=(inline o: A): Boolean = raw(a) <= raw(o)
inline infix def +(inline o: A): A = apply(raw(a) + raw(o))
inline infix def -(inline o: A): A = apply(raw(a) - raw(o))
inline def atLeast(inline bot: A): A = apply(Math.max(raw(a), raw(bot)))
inline def atMost(inline top: A): A = apply(Math.min(raw(a), raw(top)))

class OpaqueLong[A](using A =:= Long) extends TotalWrapper[A, Long]
class OpaqueDouble[A](using A =:= Double) extends TotalWrapper[A, Double]:
extension (inline a: A) inline def +(inline o: Int): A = apply(raw(a) + o)
trait OpaqueFloat[A](using A =:= Float) extends TotalWrapper[A, Float]
class OpaqueFloat[A](using A =:= Float) extends TotalWrapper[A, Float]

import scala.concurrent.duration.FiniteDuration
trait OpaqueDuration[A](using A =:= FiniteDuration) extends TotalWrapper[A, FiniteDuration]
class OpaqueDuration[A](using A =:= FiniteDuration) extends TotalWrapper[A, FiniteDuration]

abstract class YesNo[A](using ev: Boolean =:= A):
val Yes: A = true
Expand Down

0 comments on commit 30ba5ea

Please sign in to comment.