Last active
April 27, 2020 15:24
-
-
Save mbloms/609080b5ac91f03e74284d51a43aae4c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package witness | |
import scala.language.implicitConversions | |
import scala.reflect._ | |
import scala.collection.immutable._ | |
/** | |
* Base trait for a Captured instance of type T | |
* TODO: Naming | |
* @tparam T | |
*/ | |
trait Witness[+T] extends Any | |
//TODO: trait Exorcist[+F[_] <: Witness[_],T] breaks implicit resolution | |
/** | |
* Type class for lifting types into a Captured | |
* @tparam F | |
* @tparam T | |
*/ | |
trait Exorcist[+F[_] <: Witness[_],T] { | |
def evictDemons(x: T): F[T] | |
} | |
type Lifter[F <: Witness] = [T] =>> Exorcist[F,T] | |
/** | |
* Fresh[T] witnesses that the instance of T in this wrapper is "fresh". | |
* | |
* The contain instance must either be guaranteed to be immutable (Frozen) | |
* or a new instance that is created every time "extract" is called. | |
* | |
* @tparam T | |
*/ | |
sealed trait Fresh[+T] extends Witness[T] { | |
self => | |
def extract: T | |
} | |
object Fresh { | |
def fresh[T: ClassTag]: Fresh[T] = | |
new Fresh[T] { | |
override def extract: T = | |
classTag[T].runtimeClass.newInstance().asInstanceOf[T] | |
} | |
// The closure must only capture Fresh values | |
def asFresh[T](closure: => T): Fresh[T] = | |
new Fresh[T] { | |
override def extract: T = closure | |
} | |
import scala.collection.mutable._ | |
implicit def shallowCloner[A: Lifter[Immutable],CC[A] <: Cloneable[CC[A]]]: Exorcist[Fresh,CC[A]] = | |
new Exorcist[Fresh,CC[A]] { | |
override def evictDemons(x: CC[A]): Fresh[CC[A]] = | |
new Fresh[CC[A]] { | |
private val copy: CC[A] = x.clone() | |
override def extract: CC[A] = copy.clone() | |
} | |
} | |
implicit def deepCloner[A,CC[A] <: IndexedSeqOps[A,CC,CC[A]]](implicit ev: Exorcist[Fresh,A]): Exorcist[Fresh,CC[A]] = | |
new Exorcist[Fresh,CC[A]] { | |
override def evictDemons(x: CC[A]): Fresh[CC[A]] = | |
new Fresh[CC[A]] { | |
private val copy: CC[Fresh[A]] = x.map(ev.evictDemons(_)) | |
override def extract: CC[A] = copy.map(_.extract) | |
} | |
} | |
} | |
// Using `T <: Serializable` breaks | |
// upper bound [_$1] =>> witness.Witness[?] | |
sealed class Serialized[+T] private (original: T) extends Fresh[T] { | |
import java.io._ | |
val serialized: Array[Byte] = { | |
val stream = new ByteArrayOutputStream() | |
val out = new ObjectOutputStream(stream) | |
out.writeObject(original) | |
out.close() | |
stream.toByteArray | |
} | |
override def extract: T = { | |
val stream = new ByteArrayInputStream(serialized) | |
val in = new ObjectInputStream(stream) | |
in.readObject.asInstanceOf[T] | |
} | |
} | |
object Serialized { | |
def apply[T <: Serializable](x: T): Serialized[T] = new Serialized[T](x) | |
def unapply[T](serialized: Serialized[T]): Some[T] = Some(serialized.extract) | |
implicit def serializer[A <: Serializable]: Exorcist[Serialized,A] = | |
new Exorcist[Serialized,A] { | |
override def evictDemons(x: A): Serialized[A] = | |
new Serialized[A](x) | |
} | |
} | |
/** | |
* Frozen[T] witnesses that the instance of T in this wrapper is immutable. | |
* This does not neccecarily mean that all T are immutable. | |
* | |
* The contained instance must be "frozen" in the sense that | |
* it must not be possible to make it mutable without making a new instance. | |
* | |
* @tparam T | |
*/ | |
sealed trait Frozen[+T] extends Fresh[T] | |
/** | |
* An instance Immutable[T] witnesses that _all_ instances of exactly T is Immutable. | |
* Unlike Frozen[T], Immutable[T] is invariant in T: | |
* Immutable[Nothing] is a subtype of Frozen[Any], but not of Immutable[Any], | |
* because that would mean all instances of Any is immutable. | |
* @tparam T | |
*/ | |
sealed abstract class Immutable[T] extends Frozen[T] | |
// This is the sickest thing ever | |
/** Lift[F] is a function T => F[T]*/ | |
type Lift[F[_]] = [T] =>> T => F[T] | |
object Immutable { | |
// I mean where did all the boilerplate go?? | |
type Primitive = scala.Double | |
| scala.Float | |
| scala.Long | |
| scala.Int | |
| scala.Char | |
| scala.Short | |
| scala.Byte | |
| scala.Boolean | |
| scala.Unit | |
type SafeBottom = scala.Null | |
| scala.Nothing | |
| scala.collection.immutable.Nil.type | |
| scala.None.type | |
type SafeContainer[_] = scala.collection.immutable.List[_] | |
| scala.Option[_] | |
def apply[T](x: T)(implicit exorcist: Exorcist[Immutable,T]): Immutable[T] = { | |
exorcist.evictDemons(x) | |
} | |
def unapply[T](pure: Immutable[T]): Some[T] = Some(pure.extract) | |
def unapply[T](x: T)(implicit ev: Exorcist[Immutable,T]): Some[T] = Some(x) | |
implicit def ImmutabilityBlesser[T](implicit bless: T => Immutable[T]): Exorcist[Immutable,T] = | |
new Exorcist[Immutable,T] { | |
override def evictDemons(x: T): Immutable[T] = bless(x) | |
} | |
// Actually, there are classes like ArrayOps that extend AnyVal, but are mutable :( | |
//implicit class ImmutableVal[T <: AnyVal](override val extract: T) extends Immutable[T] | |
//TODO: Think hard about value classes | |
implicit class ImmutablePrimitive[T <: Primitive](override val extract: T) extends Immutable[T] | |
implicit class ImmutableBottom[T <: SafeBottom](override val extract: T) extends Immutable[T] | |
implicit class ImmutableContainer[F <: SafeContainer, T: Lift[Immutable]](override val extract: F[T]) extends Immutable[F[T]] | |
} | |
/** | |
* A Freezer takes an instance of type T, which might not be immutable, | |
* and returns an instance of T wrapped in Immutable. | |
* The returned instance might be a copy to guarantee immutability | |
* @tparam T | |
*/ | |
type Freezer[T] = Exorcist[Frozen,T] | |
object Frozen { | |
import scala.collection.ArrayOps | |
import scala.reflect.ClassTag | |
def apply[T](x: T)(implicit freezer: Freezer[T]): Frozen[T] = freezer.evictDemons(x) | |
implicit def BlessedFreezer[T](implicit blesser: Exorcist[Immutable,T]): Exorcist[Frozen,T] = blesser | |
private def asFrozen[T](frozen: T): Frozen[T] = new Frozen[T] { | |
override def extract: T = frozen | |
} | |
//private class FrozenInstance[+T](override val extract: T) extends Immutable[T] | |
//implicit class ImplicitFreezer[T](implicit convert: T => Frozen[T]) extends Freezer[T] { | |
// override def evictDemons(x: T): Frozen[T] = convert(x) | |
//} | |
implicit def ArrayOpsFreezer[A](implicit freezer: Freezer[A], classTag: ClassTag[A]): Freezer[ArrayOps[A]] = | |
new Freezer[ArrayOps[A]] { | |
override def evictDemons(xs: ArrayOps[A]): Frozen[ArrayOps[A]] = { | |
val copy = xs.toArray | |
asFrozen(new ArrayOps[A](copy)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment