How to translate type projections into PDTs in implicits? - scala

Here is one idiomatic scala 2 example:
trait Box {
type Content
val content :Content
}
implicit class BoxExtension[B <: Box](private val self :B) extends AnyVal {
def map[O](f :self.Content => O)(implicit mapper :Mapper[B, O]) :mapper.Res =
mapper(self)(f)
}
trait Mapper[B <: Box, O] {
type Res
def apply(box :B)(f :box.Content => O) :Res
}
As you see, it has already been partly converted to the usage of path dependent types. But how to create an implicit value of Mapper for an O type which references self.Content (such as O =:= self.Content itself)?. Ideally a solution which would have straightforward equivalence between Scala 2 and Scala 3.

You can make an implicit def for this. Since Dotty doesn't allow type projections on abstract types, you'll need an extra type parameter. Also, I had to make self public because it was used in the signature of map.
object Mapper {
implicit def uselessMapper[B <: Box{type Content = C}, C]: Mapper[B, C] { type Res = AllPurposeBox[C] } =
new Mapper[B, C] {
type Res = AllPurposeBox[C]
def apply(box: B)(f: box.Content => C) =
new AllPurposeBox(f(box.content))
}
}
class AllPurposeBox[T](override val content: T) extends Box {
type Content = T
}
Full example
I would normally suggest using type parameters for Box and (and an extra one for Mapper) instead, but it gets a little complicated. Perhaps you could turn BoxExtension into something like this, with C as an extra parameter:
implicit class BoxExtension[B <: Box {type Content = C}, C](private val self: B) extends AnyVal {
def map[O](f: C => O)(implicit mapper: Mapper[B, O]): mapper.Res =
mapper(self)(f)
}
If you're open to using just Dotty and not cross-compiling, you can do this
trait Mapper[B <: Box, O] {
type Res
def apply(box: B)(f: box.Content => O): Res
extension (self: B) def map(f: self.Content => O): Res = apply(self)(f)
}
object Mapper {
given uselessMapper[B <: Box{type Content = C}, C] as Mapper[B, C] {
type Res = AllPurposeBox[C]
def apply(box: B)(f: box.Content => C) = new AllPurposeBox(f(box.content))
}
}

Related

How to combine type parameter bounds and functors using Cats?

I am encountering a number of use cases where I am end of attempting to write Functor, Applicative, Monad, etc instances in contexts where I am also using type parameter bounds.
For example...
import cats._
trait Preference[A] extends Order[A]
trait SocialWelfareFunction[-CC <: Iterable[P], +P <: Preference[_]]
extends (CC => P)
object SocialWelfareFunction {
def functor: Functor[({ type F[P <: Preference[_]] = SocialWelfareFunction[Iterable[P], P] })#F] = {
???
}
...when I try to compile this I will get the following error.
kinds of the type arguments ([P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]) do not conform to the expected kinds of the type parameters (type F) in trait Monad.
[P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]'s type parameters do not match type F's expected parameters:
type P's bounds <: Playground.this.Preference[_] are stricter than type _'s declared bounds >: Nothing <: Any
How can I write Functor, Applicative, Monad , etc instances for contexts in which I am also using type parameters? Is it even possible? Is there a more appropriate way forward?
(Not a full solution, just a hint requested by OP to further clarify the question)
This example shows how to define Functor instances for type constructors that look almost as if they could be functors, but have several restrictions:
Upper type bound <: UB on type parameter
Require instances of typeclass TC
Here is a way around these two restrictions:
import scala.language.higherKinds
// Your favorite flavour of `Functor`,
// e.g. from `scalaz` or `cats`
trait Functor[F[_]] {
def map[A, B](x: F[A], f: A => B): F[B]
}
// an upper bound
trait UB
// a typeclass
trait TC[X]
// A type constructor that is not a
// functor, because it imposes upper bounds
// on the parameter, and because it requires
// a typeclass `TC` for `X`.
class Foo[X <: UB : TC] {
def map[Y <: UB : TC](f: X => Y): Foo[Y] = ??? /*
some very clever implementation making use of `UB` and `TC`
*/
}
// A Functor that approximates `Foo[X]` for *arbitrary* `X`,
// without any restrictions.
abstract class WrappedFoo[X] { outer =>
type Base <: UB
val base: Foo[Base]
protected val path: Base => X
def map[Y](f: X => Y): WrappedFoo[Y] = new WrappedFoo[Y] {
type Base = outer.Base
val base = outer.base
val path: Base => Y = outer.path andThen f
}
def unwrap[Y <: UB](
implicit
xIsY: X =:= Y,
yTC: TC[Y]
): Foo[Y] = base.map(outer.path andThen xIsY)
}
// Functor instance for `WrappedFoo`
object WrappedFooFunctor extends Functor[WrappedFoo] {
def map[A, B](x: WrappedFoo[A], f: A => B): WrappedFoo[B] = {
x map f
}
def wrap[A <: UB](foo: Foo[A]): WrappedFoo[A] = new WrappedFoo[A] {
type Base = A
val base = foo
val path = identity[A]
}
}
object Example {
// two "good" classes that conform to
// the upper bound and have instances
// of the typeclass
class Good1 extends UB
class Good2(i: Int) extends UB
implicit object Good1TC extends TC[Good1]
implicit object Good2TC extends TC[Good2]
val x1 = new Foo[Good1] // start with "Foo[Good1]"
val f: Good1 => Int = _.toString.length
val g: Int => Good2 = i => new Good2(i)
// Now we would like to go like this:
//
// Foo[Good1] ---f---> Foo[Int] ---g---> Foo[Good2]
//
// The problem is: `Int` does not conform to `UB`,
// and has no `TC` instance.
// Solution:
val x1w = WrappedFooFunctor.wrap(x1)
val intermediate = x1w.map(f) // approximates "Foo[Int]"
val x2w = intermediate.map(g) // wraps "Foo[Good2]"
val x2 = x2w.unwrap // only "Foo[Good2]"
}

How does Dotty desugar polymorphic methods?

Dotty reportedly desugars classes with type parameters to classes with type members, for example:
class C[T, U] { }
// <=>
class C {
type C$T
type C$U
}
How does Dotty desugar polymorphic methods like in the following example?
def m[T, U](x: T, u: U): T = x
// <=>
?
Unlike polymorphic classes, polymorphic methods are not desugared. They fundamentally stay polymorphic.
You may want to start at the bottom of page 13, section 5.2 of the most recent DOT paper The Essence of DOT by Nada Amin, Samuel Grütter, Martin Odersky, Tiark Rompf, and Sandro Stucki. It shows the implementation of a simple covariant polymorphic List[+A] type in Scala. Of particular note is the polymorphic cons[A] method:
package scala.collection.immutable
trait List[+A] {
def isEmpty: Boolean
def head: A
def tail: List[A]
}
object List {
def cons[A](hd: A, tl: List[A]) = new List[A] {
def isEmpty = false
def head = hd
def tail = tl
}
}
And how it is encoded in DOT:
let scala_collection_immutable = ν(sci) {
List = μ(self: {A; isEmpty: bool.Boolean; head: self.A; tail: sci.List∧{A <: self.A}})
cons: ∀(x: {A})∀(hd: x.A)∀(tl: sci.List∧{A <: x.A})sci.List∧{A <: x.A} =
λ(x: {A})λ(hd: x.A)λ(tl: sci.List∧{A <: x.A})
let result = ν(self) {
A = x.A; isEmpty = bool.false; head = hd; tail = tl }
in result
}: { μ(sci: {
List <: μ(self: {A; head: self.A; tail: sci.List∧{A <: self.A}})
cons: ∀(x: {A})∀(hd: x.A)∀(tl: sci.List∧{A <: x.A})sci.List∧{A <: x.A}
})}
in …
Which in turn should give you an intuition how it is encoded in Dotty.
Page 15 then shows you how the desugared DOT can be mapped back to Scala:
object scala_collection_immutable { sci =>
trait List { self =>
type A
def isEmpty: Boolean
def head: self.A
def tail: List{type A <: self.A}
}
def cons(x: {type A})(hd: x.A)(tl: sci.List{type A <: x.A})
: sci.List{type A <: x.A} = new List{ self =>
type A = x.A
def isEmpty = false
def head = hd
def tail = tl
}
}
As you can see, the encoding of a polymorphic method is more or less the same as a polymorphic trait: the type parameter becomes an abstract type member, in this case an abstract type member of a refinement type (aka structural type):
x : A
// becomes
x : {type A}

Scala: higher-kinded types, type projections and type mismatch error

I have the following code:
trait M[Type[_]]{
type T[X] = Type[X]
def from[A](f: T[A]): A
}
class ListM extends M[List]{ def from[A](f: T[A]) = f.head }
class Trans[A, X[_], B <: M[X]](val r: X[Option[A]])
trait CurriedTrans[X[_], B <: M[X]]{ type Type[A] = Trans[A, X, B] }
class TransM[X[_], B <: M[X]](val b: B) extends M[CurriedTrans[X, B]#Type]{
def from[A] = (f: T[A]) => b.from(f.r).get
}
and I can instantiate variables of type TransM in two ways:
val x1 = new TransM[List, ListM](new ListM)
val x2 = new TransM[ListM#T, ListM](new ListM)
I think ListM#T is redundant type parameter, so I'm trying to eliminate it:
trait M{
type T[X]
def from[A](f: T[A]): A
}
class ListM extends M {
type T[X] = List[X]
def from[A](f: T[A]) = f.head
}
class Trans[A, B <: M](val r: B#T[Option[A]])
class TransM[B <: M](val b: B) extends M {
type T[X] = Trans[X, B]
def from[Y] = (f: T[Y]) => b.from(f.r).get
}
to instantiate variable as
val x = new TransM[ListM](new ListM)
Unfortunately, the second implementation can't be compiled because of a type mismatch error:
type mismatch;
found : f.r.type (with underlying type B#T[Option[Y]])
required: TransM.this.b.T[?]
def from[Y] = (f: T[Y]) => b.from(f.r).get
^
Can I fix this issue and simplify my code or should I write boilerplate ListM#T everywhere?
#ziggystar says it: Drop the bound B and use M[X] directly:
class TransM[X[_]](val b: M[X]) extends M[CurriedTrans[X, M[X]]#Type] {
def from[A](f: T[A]) = b.from(f.r).get
}
val x1 = new TransM(new ListM)
You could consider to do the same for Trans and CurriedTrans. If you need the inner type of M, you can always expose it through a type member of Trans and CurriedTrans.

Constraining an operation by matching a type parameter to an argument's path-dependent type

I would like to exploit Scala's type system to constrain operations in a system where there are versioned references to some values. This is all happening in some transactional context Ctx which has a version type V attached to it. Now there is a Factory to create reference variables. They get created with a creation version attached them (type parameter V1), corresponding to the version of the context in which the factory was called.
Now imagine that some code tries to access that reference in a later version, that is using a different Ctx. What I want to achieve is that it is prohibited to call access on that Ref in any version (Ctx's V type field) that doesn't match the creation version, but that you are allowed to resolve the reference by some substitution mechanism that returns a new view of the Ref which can be accessed in the current version. (it's ok if substitute is called with an invalid context, e.g. one that is older than the Ref's V1 -- in that case a runtime exception could be thrown)
Here is my attempt:
trait Version
trait Ctx {
type V <: Version
}
object Ref {
implicit def access[C <: Ctx, R, T](r: R)(implicit c: C, view: R => Ref[C#V, T]): T =
view(r).access(c)
implicit def substitute[C <: Ctx, T](r: Ref[_ <: Version, T])
(implicit c: C): Ref[C#V, T] = r.substitute(c)
}
trait Ref[V1 <: Version, T] {
def access(implicit c: { type V = V1 }): T // ???
def substitute[C <: Ctx](implicit c: C): Ref[C#V, T]
}
trait Factory {
def makeRef[C <: Ctx, T](init: T)(implicit c: C): Ref[C#V, T]
}
And the problem is to define class method access in a way that the whole thing compiles, i.e. the compound object's access should compile, but at the same time that I cannot call this class method access with any Ctx, only with one whose version matches the reference's version.
Preferably without structural typing or anything that imposes performance issues.
FYI, and to close the question, here is another idea that I like because the client code is fairly clutter free:
trait System[A <: Access[_]] {
def in[T](v: Version)(fun: A => T): T
}
trait Access[Repr] {
def version: Version
def meld[R[_]](v: Version)(fun: Repr => Ref[_, R]): R[this.type]
}
trait Version
trait Ref[A, Repr[_]] {
def sub[B](b: B): Repr[B]
}
object MyRef {
def apply[A <: MyAccess](implicit a: A): MyRef[A] = new Impl[A](a)
private class Impl[A](a: A) extends MyRef[A] {
def sub[B](b: B) = new Impl[B](b)
def schnuppi(implicit ev: A <:< MyAccess) = a.gagaism
}
}
trait MyRef[A] extends Ref[A, MyRef] {
// this is how we get MyAccess specific functionality
// in here without getting trapped in more type parameters
// in all the traits
def schnuppi(implicit ev: A <:< MyAccess): Int
}
trait MyAccess extends Access[MyAccess] {
var head: MyRef[this.type]
var tail: MyRef[this.type]
def gagaism: Int
}
def test(sys: System[MyAccess], v0: Version, v1: Version): Unit = {
val v2 = sys.in(v0) { a => a.tail = a.meld(v1)(_.head); a.version }
val a3 = sys.in(v2) { a => a }
val (v4, a4) = sys.in(v1) { a =>
a.head = a.head
println(a.head.schnuppi) // yes!
(a.version, a)
}
// a3.head = a4.head // forbidden
}
The following seems to work:
trait Version
trait Ctx[+V1 <: Version] {
type V = V1
}
type AnyCtx = Ctx[_ <: Version]
type AnyRf[T] = Ref[_ <: Version, T]
object Ref {
implicit def access[C <: AnyCtx, R, T](r: R)(
implicit c: C, view: R => Ref[C#V, T]): T = view(r).access(c)
implicit def substitute[C <: AnyCtx, T](r: AnyRf[T])(implicit c: C): Ref[C#V, T] =
r.substitute( c )
}
trait Ref[V1 <: Version, T] {
def access(implicit c: Ctx[V1]): T
def substitute[C <: AnyCtx](implicit c: C): Ref[C#V, T]
}
trait Factory {
def makeVar[C <: AnyCtx, T](init: T)(implicit c: C): Ref[C#V, T]
}
// def shouldCompile1(r: AnyRf[String])(implicit c: AnyCtx): String = r
def shouldCompile2(r: AnyRf[String])(implicit c: AnyCtx): String = {
val r1 = Ref.substitute(r)
r1.access(c)
}
// def shouldFail(r: AnyRf[String])(implicit c: AnyCtx): String = r.access(c)
So the follow-up questions are
why I need a redundancy of the type
parameter for Ctx to achieve this. I hate that these type
parameters accumulate like rabbits in my code.
why shouldCompile1 doesn't compile
—can i get the implicits to work as planned?
EDIT:
This is wrong, too. The variance annotation is wrong. Because now the following compiles although it shouldn't:
def versionStep(c: AnyCtx): AnyCtx = c // no importa
def shouldFail3[C <: AnyCtx](f: Factory, c: C): String = {
val r = f.makeVar("Hallo")(c)
val c2 = versionStep(c)
r.access(c2)
}

Unpacking tuple types in Scala

I was just wondering, can I decompose a tuple type into its components' types in Scala?
I mean, something like this
trait Container {
type Element
}
trait AssociativeContainer extends Container {
type Element <: (Unit, Unit)
def get(x : Element#First) : Element#Second
}
You can't unpack, per se, but maybe this achieves what you want:
type First
type Second
type Element = (First, Second)
def get(x: First): Second
This doesn't unpack the types, but it does constrain the types A and B when calling get.
trait Container {
type Element
}
trait AssociativeContainer extends Container {
type Element <: Tuple2[_, _]
def get[A, B](x: A)(implicit ev: (A, B) =:= Element): B
}
This looks promising, but is cheating -- it doesn't work if Element is abstract.
def Unpack[T<:Tuple2[_, _]] = new {
def apply[A, B](implicit ev: T <:< (A, B)) = new {
type AA = A
type BB = B
}
}
trait AssociativeContainer {
type Element = (Int, String)
val unpacked = Unpack[Element].apply
type A = unpacked.AA
type B = unpacked.BB
1: A
"": B
def get(x: A): B
}
I'm a bit late to this, but what about using pattern matching? Doesn't have quite the correct return type, and my syntax might be a bit off, but here goes:
def get[K](key: K): Iterable[Any] {
for ((key, x) <- elements) yield x
}