Skip to content

Instantly share code, notes, and snippets.

@rudogma
Last active January 1, 2019 17:24
Show Gist options
  • Save rudogma/58f330db4a9c79f56f968587b49d9f1d to your computer and use it in GitHub Desktop.
Save rudogma/58f330db4a9c79f56f968587b49d9f1d to your computer and use it in GitHub Desktop.
Newtype upgrading for implicits & implicit conversions without imports
import scala.language.higherKinds
import scala.language.implicitConversions
object TestNewtypes extends App {
/**
Changed lines:
type NewType = Newtype[T, Tag0] with superduper.Tag[T, this.type]
def apply[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): NewType = c.asInstanceOf[T with NewType]
def @@[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): NewType = c.asInstanceOf[T with NewType]
*/
import superduper._
implicit class StepOps(val __v:Int) {
def next = Step(__v + 1)
def +(v2:Int):Step = Step @@ (__v + v2 * 2)
}
object Step extends NewType[Int, StepOps] {
implicit def koko:Koko[NewType] = new Koko[NewType] {
def test():String = "hell"
}
implicit def kokoconversion(value:NewType):Step2 = Step2(raw(value))
}
type Step = Step.NewType
implicit class StepOps2(val __v:Int) {
def next = Step2(__v + 2)
def +(v2:Int):Step2 = Step2 @@ (__v + v2 * 3)
}
object Step2 extends NewType[Int, StepOps2] {
}
type Step2 = Step2.NewType
trait Koko[T]{
def test():String
}
//Test
val x0:Step = Step(5)
println(s"x0: ${x0}") // prints 5
println(s"x0.next: ${x0.next}") // prints 6
val x1 = x0 + 5
println(s"x1: ${x1}") // prints 15 (+ from Ops)
// implicit typeclass found without import
val koko1 = implicitly[Koko[Step]]
val x2:Step2 = x0
println(s"x2: ${x2}") // implicitconversion without import. prints 5
val x3 = x2 + 1
println(s"x2.next: ${x2.next}, x3: ${x3}") // prints '7 8' (+ from Ops2)
}
object superduper {
private def cast[T, U](v: U): T = v.asInstanceOf[T]
/**
* Making universal trait for overcoming `feature bug`(knowing as `[I cannot be cast to [Ljava.lang.Object;` - see at TestScalacBug).
* Everything seems ok for now. There is no runtime overhead(after jit). Casting doesn't change memory model, boxing for primitive types appears only at specific corner cases and JIT cleans generated rubbish bytecode very well
**/
// type Tag[T, +U] = {type Raw = T; type Gag <: U}
sealed trait Tag[T, +U] extends Any {
type Raw = T;
type Gag <: U
}
type @@[T, +U] = T with Tag[T, U]
type Tagged[T, +U] = T with Tag[T, U]
/**
* `Classic` way. Original idea: Miles Sabin
**/
trait ClassicTagger[U] {
def apply[T](v: T): T @@ U = cast(v)
}
private val classicStub = new ClassicTagger[Nothing] {}
def tag[U]:ClassicTagger[U] = cast(classicStub)
def @@[U]:ClassicTagger[U] = cast(classicStub)
def untag[T, U](value: T @@ U): T = value
/** -- end classic -- **/
/** Lift ( Adopted and simplified from: https://github.com/softwaremill/scala-common/blob/master/tagging/src/main/scala/com/softwaremill/tagging/TypeclassTaggingCompat.scala) **/
trait LifterF[F[_]] {
implicit def lift[T, U](implicit f: F[T]): F[T @@ U] = cast(f)
}
implicit def liftLifterF[F[_], T, U](implicit f: F[T], lifter: LifterF[F]): F[T @@ U] = cast(f)
object lifterF {
def apply[F[_]] = new LifterF[F] {}
implicit def liftAnyF[T, U, F[_]](implicit f: F[T]): F[T @@ U] = cast(f)
}
/** Preparing for Magic **/
/**
* Example:
* ```
* object Width extends TaggedType[Int]
* type Width = Width.Type // this for pretty coding: ```def method(v:Width)```
* ```
*/
trait TaggedType[T] {
// sealed trait Tag
/**
* Original idea from Alexander Semenov [https://github.com/Tvaroh] using inner:
*
* ```sealed trait Tag```
*
* but, we can use existent already materialized anchor ```object MyTag extends TaggedType[T]```, so there is no need
* for any additional anchor trait. All right for now :)
*
*/
type Tag <: this.type
type Raw = T
type Type = T @@ Tag
/**
* Adds one more tag to existing tags (if no tags then adds one)
*/
def apply[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): tagger.Out = cast(c)
/**
* Alias for `apply` for pretty coding `MyTag @@ value`
*/
def @@[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): tagger.Out = cast(c)
/**
* Replaces all existing tags with 1 new (if no tags then adds one)
* (Don't know who really needs this, but very simple implementation, so it is here)
*/
def !@@[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): tagger.OutReplaced = cast(c)
/**
* Removes concrete tag (this.Tag)
*/
def untag[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag, Sub, C]): tagger.Untagged = cast(c)
def raw(c:Type):T = c
// def tagRaw(raw:T):T @@ Tag = cast(raw)
implicit def ordering[U](implicit origin:Ordering[T]):Ordering[T @@ U] = cast(origin)
}
/**
* New name: Overtagged
*/
class OverTagged[R, T <: TaggedType[R]](val nested:T with TaggedType[R]) extends TaggedType[T#Type]{
// override type Tag = T#Tag with this.type
}
/**
* Need one more trait in chain. Do not cut and optimize it!
*/
/**
* Temporary, subject to change in future
* For cases where ONLY tagged type parameterized by type. See examples in TestBoundedTaggedTypes.scala
*/
trait TaggedTypeF {
trait TypeF[T] extends TaggedType[T]
private lazy val stub = new TypeF[Nothing] {}
type Type[T] = TypeF[T]#Type
def apply[T]:TypeF[T] = cast(stub)
}
/**
* Temporary, subject to change in future
* For cases where both tagged type and base with parameterized by type. See examples in TestBoundedTaggedTypes.scala
*/
trait TaggedTypeFF[F[_]] {
trait TypeF[T] extends TaggedType[T]
private lazy val stub = new TypeF[Nothing] {}
type Type[T] = TypeF[F[T]]#Type
def apply[T]:TypeF[F[T]] = cast(stub)
}
/**
* NEW TYPES (based on Miles Sabin shapeless.newtype )
*/
//Needs anonymous {}, because `trait` will be materialized and will not compile
type Newtype[Repr, Ops] = { type T = Tag[Repr, Ops] }
implicit def newtypeOps[Repr, Ops](t : Newtype[Repr, Ops])(implicit mkOps : Repr => Ops) : Ops = t.asInstanceOf[Repr]
trait NewType[T, Tag0] {
type Tag = Tag0
type Raw = T
type Type = T @@ Tag0
type NewType = Newtype[T, Tag0] with superduper.Tag[T, this.type]
def apply[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag0, Sub, C]): NewType = c.asInstanceOf[T with NewType]
def @@[TagIn, Sub, C](c: C)(implicit tagger: Tagger[TagIn, Type, Tag0, Sub, C]): NewType = c.asInstanceOf[T with NewType]
def raw(c:NewType):T = c.asInstanceOf[T]
}
trait NewTypeF[R, Tag] extends NewType[R, Tag]
private val newTypeFStub = new NewTypeF[Nothing, Nothing] {}
def NewTypeF[Raw,Tag]:NewTypeF[Raw,Tag]= cast(newTypeFStub)
/** --- END NEW TYPES --- **/
/**
* For pretty coding `value @@ MyTag`
*/
implicit class PostfixSugar[C](val __c: C) extends AnyVal {
def @@[TagIn, Raw, Sub](typ: TaggedType[Raw])(implicit tagger: Tagger[TagIn, typ.Type, typ.Tag, Sub, C]): tagger.Out = cast(__c)
def !@@[TagIn, Raw, Sub](typ: TaggedType[Raw])
(implicit tagger: Tagger[TagIn, typ.Type, typ.Tag, Sub, C]): tagger.OutReplaced = cast(__c)
def untag[TagIn, Raw, Sub](typ: TaggedType[Raw])
(implicit tagger: Tagger[TagIn, typ.Type, typ.Tag, Sub, C]): tagger.Untagged = cast(__c)
}
/** Magic starts here **/
trait Tagger[TagIn, Tag, U, SubType, C] {
type Out
type OutReplaced
type Untagged
type NewType
}
object Tagger {
private val dummyTaggerStub = new Tagger[Nothing, Nothing, Nothing, Nothing, Nothing] {}
def dummyTagger[T]: T = cast(dummyTaggerStub)
type Aux[Out0, OutR0, Untagged0, NewType0, TagIn, Tag, U, SubType, C] = Tagger[TagIn, Tag, U, SubType, C] {
type Out = Out0
type OutReplaced = OutR0
type Untagged = Untagged0
type NewType = NewType0
}
implicit def recurС2[TagNew, TaggedNew <: Tagged[_, TagNew], TagIn, SubType, InnerC[_], OuterC[_]](implicit nested: Tagger[TagIn, TaggedNew, TagNew, SubType, InnerC[SubType]]): Aux[OuterC[nested.Out], OuterC[nested.OutReplaced], OuterC[nested.Untagged], OuterC[nested.NewType], TagIn, TaggedNew, TagNew, InnerC[SubType], OuterC[InnerC[SubType]]] = dummyTagger
implicit def recurС[TagNew, TaggedNew <: Tagged[Raw, TagNew], TagIn, Raw, InnerC[_], OuterC[_]](implicit nested: Tagger[TagIn, TaggedNew, TagNew, Raw, InnerC[Raw @@ TagIn]]): Aux[OuterC[InnerC[Raw @@ (TagIn with TagNew)]], OuterC[InnerC[Raw @@ TagNew]], OuterC[InnerC[Raw]], OuterC[InnerC[Newtype[Raw,TagNew]]], TagIn, TaggedNew, TagNew, InnerC[Raw @@ TagIn], OuterC[InnerC[Raw @@ TagIn]]] = dummyTagger
implicit def baseС[TagIn, TagNew, TaggedNew <: Tagged[Raw, TagNew], Raw, C[_]]: Aux[C[Raw @@ (TagIn with TagNew)], C[Raw @@ TagNew], C[Raw], C[Newtype[Raw,TagNew]], TagIn, TaggedNew, TagNew, Raw, C[Raw @@ TagIn]] = dummyTagger
implicit def baseСRaw[TagNew, TaggedNew <: Tagged[Raw, TagNew], Raw, C[_]]: Aux[C[Raw @@ TagNew], C[Raw @@ TagNew], C[Raw], C[Newtype[Raw,TagNew]], TagNew, TaggedNew, TagNew, Raw, C[Raw]] = dummyTagger
implicit def baseTagged[TagIn, TagNew, TaggedNew <: Tagged[Raw, TagNew], Raw]: Aux[Raw @@ (TagIn with TagNew), Raw @@ TagNew, Raw, Newtype[Raw,TagNew], TagIn, TaggedNew, TagNew, Raw, Raw @@ TagIn] = dummyTagger
implicit def baseRaw[TagNew, TaggedNew <: Tagged[Raw, TagNew], Raw]: Aux[Raw @@ TagNew, Raw @@ TagNew, Raw, Newtype[Raw,TagNew], TagNew, TaggedNew, TagNew, Raw, Raw] = dummyTagger
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment