A common gotcha when working with futures is that when you expect Future[Unit], even Future[Future[Unit]] will be accepted (see e.g. Why Shouldn’t You Use Future[Unit] as a Return Type in a Scala Program).
I was surprised recently Future.sequence(setOfFutures) is not accepted in such situation:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
val set = Set(Future(()))
def fuu: Future[Unit] = {
Future.sequence(set)
}
With Scala 2.12.13 I get error:
type mismatch;
found : scala.concurrent.Future[scala.collection.immutable.Set[Unit]]
With Scala 2.13 I get:
Cannot construct a collection of type Unit with elements of type Unit based on a collection of type scala.collection.immutable.Set[scala.concurrent.Future[Unit]].
When I change the body of the function to:
val s = Future.sequence(set)
s
I get set the same error as before.
Why is Future[Future[Unit]] accepted as a Future[Unit] and Future[Set[Unit]] or Future[List[Unit]] is not?
Consider the signature of Future.sequence in Scala 2.13
def sequence[A, CC[X] <: IterableOnce[X], To](in: CC[Future[A]])(
implicit
bf: BuildFrom[CC[Future[A]], A, To],
executor: ExecutionContext
): Future[To]
so given
val set = Set(Future(()))
def fuu: Future[Unit] = Future.sequence(set)
then inference will assign type paramers of sequence like so
To = Unit
A = Unit
CC = Set
For example consider fuu's return type Future[Unit] = Future[To]. Hence we have
def fuu: Future[Unit] = Future.sequence[Unit, Set, Unit](set)
so compiler needs to implicitly assign bf parameter
scala> implicitly[BuildFrom[Set[Future[Unit]], Unit, Unit]]
^
error: Cannot construct a collection of type Unit with elements of type Unit based on a collection of type Set[scala.concurrent.Future[Unit]].
Now consider Scala 2.12 signature of Future.sequence
def sequence[A, M[X] <: TraversableOnce[X]](in: M[Future[A]])(
implicit
cbf: CanBuildFrom[M[Future[A]],A,M[A]],
executor: ExecutionContext
): Future[M[A]]
so given
val set = Set(Future(()))
def fuu: Future[Unit] = Future.sequence(set)
inference becomes
A = Unit
M = Set
so we have
def fuu: Future[Unit] = Future.sequence[Unit, Set](set)
where compiler can successfully implicitly assign cbf parameter
scala> implicitly[CanBuildFrom[Set[Future[Unit]],Unit,Set[Unit]]]
res4: scala.collection.generic.CanBuildFrom[Set[scala.concurrent.Future[Unit]],Unit,Set[Unit]] = scala.collection.generic.GenSetFactory$$anon$1#1bff70a6
hence we effectively have in 2.12 the following situation
scala> def fuu: Future[Unit] = Future.sequence(set) : Future[Set[Unit]]
<console>:25: error: type mismatch;
found : scala.concurrent.Future[Set[Unit]]
required: scala.concurrent.Future[Unit]
def fuu: Future[Unit] = Future.sequence(set) : Future[Set[Unit]]
This should explain the difference between the two compiler error messages between the two Scala versions is not related to value discarding but to how inference assigned the corresponding types.
Related
Assuming that I intend to use the singleton/literal type feature in a scala program, this feature is provided in shapeless library in scala 2.12 (scala 2.13 supports native literal type but let's use shapeless as an example)
In shapeless, literal type is represented as a path-dependent inner type of Witness object, which can be implicitly converted from a scala literal/const:
import com.tribbloids.spike.BaseSpec
import shapeless.Witness
import scala.util.Random
val w: Witness.Lt[Int] = 3
val w2: Witness.Lt[Int] = Random.nextInt(3) // this doesn't compile
The second line cause compilation to throw an exception:
[Error] .../WitnessSuite.scala:14: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
one error found
Now, assuming that I want to write something like Option[Witness.Lt[Int]] that can be converted from an Int if it is a literal or not. In scala type class convention I should write something like this:
trait MayHaveWitness {
type Lit
}
trait MayHaveWitness_Implicits0 {
class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {
type Lit = w.T
}
object None extends MayHaveWitness {
type Lit = Nothing
}
implicit def fromNonLit(v: Int): None.type = None
}
object MayHaveWitness extends MayHaveWitness_Implicits0 {
implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)
}
val v1: MayHaveWitness = 3
println(v1.getClass)
val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)
MayHaveWitness_Implicits0 is of lower level and theoretically should be overshadowed by fromLit if the Witness implicit conversion is successful. Unfortunately when I execute this code all I got was:
class com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$
class com.tribbloids.spike.shapeless_spike.WitnessSuite$MayHaveWitness_Implicits0$1$None$
The Witness implicit conversion never happens. My questions are:
why implicit proof: T => Witness.Lt[Int] is not a successful summoner of the following shapeless macro?
implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl
how do I use type classes & other scala features to implement this smooth fallback of type-level deduction? preferrably:
NOT using macro
If not possible, NOT using whitebox macro
If also not impossible, NOT using macro that will be discarded by dotty
Shapeless defines implicit instance of type Witness.Aux[T]
implicit def apply[T]: Witness.Aux[T] = macro SingletonTypeMacros.materializeImpl[T]
and implicit conversion from type T to Witness.Lt[T]
implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl
Implicit instance Witness.Aux[T] is resolved or not based on type T only (whether T is a singleton type or nor) like implicit instances of ordinary type classes. But implicit conversion T => Witness.Lt[T] is not like ordinary implicit conversions. Ordinary implicit conversions are resolved or not based on type of a value to be conversed. But T => Witness.Lt[T] is resolved or not based not only on the type T but also on the value t itself (whether t is constant/stable or not).
If you switch on scalacOptions ++= Seq("-Ymacro-debug-lite", "-Xlog-implicits") you'll see that in
val w: Witness.Lt[Int] = 3 //compiles
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](3) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-9,offset=205
//Warning:scalac: _root_.shapeless.Witness.mkWitness[Int(3)](3.asInstanceOf[Int(3)])
val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](scala.util.Random.nextInt(3)) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-10,offset=249
//Warning:scalac: macro expansion has failed: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
//Error: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
only implicit def apply[T](t: T): Witness.Lt[T] was checked (and worked in w but didn't work in w2).
Also in
val v1: MayHaveWitness = 3 // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
// (which expands to) Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int(3) => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
and in
val v2: MayHaveWitness = Random.nextInt(3) // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
// (which expands to) Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
both implicit def apply[T]: Witness.Aux[T] and implicit def apply[T](t: T): Witness.Lt[T] were checked and none of them worked.
why implicit proof: T => Witness.Lt[Int] is not a successful summoner of the following shapeless macro?
Compiler treats implicits of functional types A => B differently than implicits of other types. It can treat them as implicit conversions (views). But whether it actually treats them as conversions or just implicit instances of type A => B (like other types) depends on boolean flag isView.
When you do
val w: Witness.Lt[Int] = 3 //compiles
val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
val v1: MayHaveWitness = 3 //compiles
val v2: MayHaveWitness = Random.nextInt(3) //compiles
isView is true. But when you do
implicitly[Int => Witness.Lt[Int]] //doesn't compile
implicitly[3 => Witness.Lt[Int]] //doesn't compile
implicitly[Int => MayHaveWitness] //doesn't compile
implicitly[3 => MayHaveWitness] //doesn't compile
or here
implicit def fromLit... (implicit proof: T => Witness.Lt[Int]) ...
______________________________________
isView is false.
In simple cases existence of implicit A => B and implicit conversion from A to B are the same
class A
class B
// implicit val aToB: A => B = null // this one
implicit def aToB(a: A): B = null // or this one
implicitly[A => B] //compiles
val b: B = new A //compiles
but not in our case. There is implicit conversion 3 => Witness.Lt[3] but not an instance of this type
val w: Witness.Lt[3] = 3.asInstanceOf[3] //compiles
implicitly[3 => Witness.Lt[3]] // doesn't compile
//Information: shapeless.this.Witness.apply is not a valid implicit value for 3 => shapeless.Witness.Lt[3] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: 3 => shapeless.Witness.Lt[3]
// (which expands to) 3 => shapeless.Witness{type T <: 3}
//Error: No implicit view available from 3 => shapeless.Witness.Lt[3].
so it checks implicit def apply[T]: Witness.Aux[T] but not implicit def apply[T](t: T): Witness.Lt[T]. I didn't debug implicit resolution deeply but I suspect that some type is not inferred before implicit is resolved.
There is no standard way to switch on isView in order to completely emulate behavior of implicit conversion while resolving proof in ... def fromLit... (implicit proof: T => Witness.Lt[Int]) .... We can switch on isView with macros if we use c.inferImplicitView rather than c.inferImplicitValue
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait ImplicitView[A, B] {
def instance: A => B
}
object ImplicitView {
implicit def mkImplicitView[A, B]: ImplicitView[A, B] = macro mkImplicitViewImpl[A, B]
def mkImplicitViewImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val tpA = weakTypeOf[A]
val tpB = weakTypeOf[B]
val x = TermName(c.freshName("x"))
val conversion = c.inferImplicitView(tree = EmptyTree, from = tpA, to = tpB, silent = false)
q"""new ImplicitView[$tpA, $tpB] {
def instance: $tpA => $tpB = ($x: $tpA) => $conversion($x)
}"""
}
Let's replace
implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)
with
implicit def fromLit[T](literal: T)(implicit proof: ImplicitView[T, Witness.Lt[Int]]): MayHaveWitness.Some = new Some(proof.instance(literal))
Also we have to modify
implicit def fromNonLit(v: Int): None.type = None
because it's ambiguous with fromLit. Reasons are similar to those. The easiest fix is to replace it with
implicit def fromNonLit[T](v: T): None.type = None
Now both
val v1: MayHaveWitness = 3
println(v1.getClass)
val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)
give Some (I suspect that's not what you wanted). That's understandable. Random.nextInt(3) is Int. And we were resolving MayHaveWitness based only on types. And there is implicit conversion Int => Witness.Lt[Int]. So it's Some.
So it seems if we want v1 to give Some and v2 to give None then we can't do that based only on types. So approach with type classes will not work and we'll have to use macros.
trait MayHaveWitness {
type Lit
}
object MayHaveWitness {
class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {
type Lit = w.T
}
object None extends MayHaveWitness {
type Lit = Nothing
}
implicit def fromLit[T](literal: T): MayHaveWitness = macro fromLitImpl[T]
def fromLitImpl[T: c.WeakTypeTag](c: whitebox.Context)(literal: c.Tree): c.Tree = {
import c.universe._
val conversion = c.inferImplicitView(tree = literal, from = weakTypeOf[T], to = typeOf[Witness.Lt[Int]], silent = false)
util.Try(c.typecheck(q"new MayHaveWitness.Some($conversion($literal))"))
.getOrElse(q"MayHaveWitness.None")
}
}
Here we replaced (implicit proof: T => Witness.Lt[Int]) with c.inferImplicitView... and we explored not only type of literal but also literal itself.
Now in
val v1: MayHaveWitness = 3
println(v1.getClass)
val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)
v1 gves Some and v2 gives None.
If you make fromLit blackbox it will still work but will return MayHaveWitness instead of MayHaveWitness.Some and MayHaveWitness.None.
Ultimately what I want to do is provide one implementation of a type class for some specific type T and another implementation for all other types which are not T. I thought (perhaps incorrectly) that the easiest way to do this would be to try type negation via ambiguous implicits as described in this question. However, if I accidentally omit the implicit type class declaration, my code will still compile (should it?) but include bugs as only one of the implementations is used.
This is how the context bound is defined:
scala> trait NotAnInt[A]
defined trait NotAnInt
scala> implicit def everythingIsNotAnInt[A]: NotAnInt[A] = new NotAnInt[A] {}
everythingIsNotAnInt: [A]=> NotAnInt[A]
scala> implicit def intsAreInts1: NotAnInt[Int] = ???
intsAreInts1: NotAnInt[Int]
scala> implicit def intsAreInts2: NotAnInt[Int] = ???
intsAreInts2: NotAnInt[Int]
scala> implicit def nothingsAreInts1: NotAnInt[Nothing] = ???
nothingsAreInts1: NotAnInt[Nothing]
scala> implicit def nothingsAreInts2: NotAnInt[Nothing] = ???
nothingsAreInts2: NotAnInt[Nothing]
At this point NotAnInt[T] is summonable for all T except Int/Nothing:
scala> implicitly[NotAnInt[String]]
res3: NotAnInt[String] = $anon$1#1a24fe09
scala> implicitly[NotAnInt[Int]]
<console>:16: error: ambiguous implicit values:
both method intsAreInts1 of type => NotAnInt[Int]
and method intsAreInts2 of type => NotAnInt[Int]
match expected type NotAnInt[Int]
implicitly[NotAnInt[Int]]
^
scala> implicitly[NotAnInt[Nothing]]
<console>:18: error: ambiguous implicit values:
both method nothingsAreInts1 of type => NotAnInt[Nothing]
and method nothingsAreInts2 of type => NotAnInt[Nothing]
match expected type NotAnInt[Nothing]
implicitly[NotAnInt[Nothing]]
^
Now I have my NotAnInt context bound defined I can create my type class with its implementations:
scala> trait IntChecker[A] { def isInt(): Boolean }
defined trait IntChecker
scala> implicit val intIntChecker: IntChecker[Int] = new IntChecker[Int] { override def isInt = true }
intIntChecker: IntChecker[Int] = $anon$1#585dd35c
scala> implicit def otherIntChecker[A: NotAnInt]: IntChecker[A] = new IntChecker[A] { override def isInt = false }
otherIntChecker: [A](implicit evidence$1: NotAnInt[A])IntChecker[A]
This type class can be used as expected:
scala> def printIntStatus[T: IntChecker](t: T): Unit = { println(implicitly[IntChecker[T]].isInt()) }
printIntStatus: [T](t: T)(implicit evidence$1: IntChecker[T])Unit
scala> printIntStatus(3)
true
scala> printIntStatus("three")
false
However, the following also compiles:
scala> def printIntStatusWithBug[T](t: T): Unit = { println(implicitly[IntChecker[T]].isInt()) }
printIntStatusWithBug: [T](t: T)Unit
scala> printIntStatusWithBug(3)
false
scala> printIntStatusWithBug("three")
false
I would not expect this second function to compile as there should be no implicit IntChecker[T] available. I expect everythingIsNotAnInt is the cause of this problem but I can't think of a way around this.
I'm interested in why this approach fails as well as alternative methods on how to achieve the same thing. Thank you.
Consider the following alternative implementation (which uses Sabin's type inequalities)
trait =!=[A, B]
implicit def neq[A, B] : A =!= B = null
implicit def neqAmbig1[A] : A =!= A = null
implicit def neqAmbig2[A] : A =!= A = null
trait IntChecker[A] {
def isInt(): Boolean
}
object IntChecker {
import scala.reflect.ClassTag
implicit val intIntChecker: IntChecker[Int] = () => true
implicit def notIntIntChecker[T: ClassTag](implicit ev: T =!= Int): IntChecker[T] = () => false
}
def printIntStatus[T: IntChecker](t: T) = implicitly[IntChecker[T]].isInt()
import IntChecker._
printIntStatus(3)
printIntStatus("three")
which outputs
res0: Boolean = true
res1: Boolean = false
however the buggy implementation where we forget IntChecker bound
def printIntStatusWithBug[T](t: T) = implicitly[IntChecker[T]].isInt()
should not compile due to having T: ClassTag bound in
implicit def notIntIntChecker[T: ClassTag](implicit ev: T =!= Int)
giving compiler error
could not find implicit value for parameter e: IntChecker[T]
def printIntStatusWithBug[T](t: T) = implicitly[IntChecker[T]].isInt()
^
Using the Scala REPL, I've defined a function that takes an Int as its first parameter and a function with this signature Int => Int as the second implicit parameter:
scala> def doer(i: Int)(implicit a: Int => Int): Int = a(i)
doer: (i: Int)(implicit a: Int => Int)Int
Why does running this function without providing the implicit parameter work?
scala> doer(4)
res1: Int = 4
Where does the implicit Int to Int function come from?
The REPL reports that there are no implicits defined:
scala> :impl
No implicits have been imported other than those in Predef.
Predef contains an implicit evidence that one type is a subtype of another: A <:< B. Every type is a subtype of itself so implicitly[Int <:< Int] works. This <:< class extends function A => B. So that's why implicitly[Int => Int] also works.
Int and java.lang.Integer however are different things with no subtype relation whatsoever, so these int2Integer implicits have nothing to do with it.
If you have a REPL of a recent Scala version you can type
scala> doer(4) //print
And press the tab key instead of enter. It will show you the desugared version doer(4)(scala.Predef.$conforms[Int]) of your code with all implicits filled in explicitly.
There are bunch of implicits defined in scala.Predef which are accessible in all Scala compilation units without explicit qualification, and one of them is
implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer]
If you check the Predef source code it is possible to see a lot of implicit functions. The compiler will just pick up a compatible one.
Some examples:
implicit def booleanArrayOps(xs: Array[Boolean]): ArrayOps[Boolean] = new ArrayOps.ofBoolean(xs)
implicit def byteArrayOps(xs: Array[Byte]): ArrayOps[Byte] = new ArrayOps.ofByte(xs)
implicit def charArrayOps(xs: Array[Char]): ArrayOps[Char] = new ArrayOps.ofChar(xs)
implicit def doubleArrayOps(xs: Array[Double]): ArrayOps[Double] = new ArrayOps.ofDouble(xs)
implicit def floatArrayOps(xs: Array[Float]): ArrayOps[Float] = new ArrayOps.ofFloat(xs)
implicit def intArrayOps(xs: Array[Int]): ArrayOps[Int] = new ArrayOps.ofInt(xs)
implicit def longArrayOps(xs: Array[Long]): ArrayOps[Long] = new ArrayOps.ofLong(xs)
implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps.ofRef[T](xs)
implicit def shortArrayOps(xs: Array[Short]): ArrayOps[Short] = new ArrayOps.ofShort(xs)
implicit def unitArrayOps(xs: Array[Unit]): ArrayOps[Unit] = new ArrayOps.ofUnit(xs)
// "Autoboxing" and "Autounboxing" ---------------------------------------------------
implicit def byte2Byte(x: Byte) = java.lang.Byte.valueOf(x)
implicit def short2Short(x: Short) = java.lang.Short.valueOf(x)
implicit def char2Character(x: Char) = java.lang.Character.valueOf(x)
implicit def int2Integer(x: Int) = java.lang.Integer.valueOf(x)
implicit def long2Long(x: Long) = java.lang.Long.valueOf(x)
implicit def float2Float(x: Float) = java.lang.Float.valueOf(x)
implicit def double2Double(x: Double) = java.lang.Double.valueOf(x)
implicit def boolean2Boolean(x: Boolean) = java.lang.Boolean.valueOf(x)
implicit def Byte2byte(x: java.lang.Byte): Byte = x.byteValue
implicit def Short2short(x: java.lang.Short): Short = x.shortValue
implicit def Character2char(x: java.lang.Character): Char = x.charValue
implicit def Integer2int(x: java.lang.Integer): Int = x.intValue
implicit def Long2long(x: java.lang.Long): Long = x.longValue
implicit def Float2float(x: java.lang.Float): Float = x.floatValue
implicit def Double2double(x: java.lang.Double): Double = x.doubleValue
implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
Implicits are designed for such purpose. The compiler will search if any implicit definition objects are available. If it finds then will use them. There are many implicit definition objects provided scala. You can also define the custom objects to be implicit and will be used if they are within the scope of your code.
See the doc
https://docs.scala-lang.org/tour/implicit-parameters.html
doer works because since it accepts implicit parameter, it is available in Predef. Predef is imported automatically to scala scope. Looks like the function used is int2Integer.
I have a couple of methods on a trait as so:
trait ResourceFactory[+R] {
def using[T](work: R => T): T
def usingAsync[T](work: R => Future[T]): Future[T]
}
Unfortunately there's nothing in the type checker to stop you calling the first using method with a function returning a Future. I'd like the compiler to insist that the type of T in the first method be anything other than Future, to prevent that mistake - is that possible?
Thanks
You can use shapeless' <:!<:
import scala.concurrent.Future
import shapeless._
trait ResourceFactory[+R] {
def using[T](work: R => T)(implicit ev: T <:!< Future[_]): T = ???
def usingAsync[T](work: R => Future[T]): Future[T] = ???
}
Then:
scala> val r = new ResourceFactory[Int] {}
r: ResourceFactory[Int] = $anon$1#effe6ad
// Compiles (the error is due to the use of ???)
scala> r.using(_.toString)
scala.NotImplementedError: an implementation is missing
// Doesn't compile
scala> r.using(Future.successful(_))
<console>:17: error: ambiguous implicit values:
both method nsubAmbig1 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
and method nsubAmbig2 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
match expected type shapeless.<:!<[scala.concurrent.Future[Int],scala.concurrent.Future[_]]
r.using(Future.successful(_))
Here's an alternative I've stolen shamelessly from https://github.com/japgolly/scalajs-react/blob/cb75721e3bbd0033ad63d380bcaddc96fbe906e3/core/src/main/scala/japgolly/scalajs/react/Callback.scala#L21-L31:
#implicitNotFound("You're returning a ${A}, which is asynchronous, which means the resource may be closed before you try and use it. Instead use usingAsync.")
final class NotFuture[A] private[ResourceFactoryTests]()
object NotFuture {
final class Proof[A] private[ResourceFactory]()
object Proof {
implicit def preventFuture1[A]: Proof[Future[A]] = ???
implicit def preventFuture2[A]: Proof[Future[A]] = ???
#inline implicit def allowAnythingElse[A]: Proof[A] = null
}
#inline implicit def apply[A: Proof]: NotFuture[A] = null
}
which can be used as:
trait ResourceFactory[+R] {
def using[T: ResourceGuard](work: R => T): T
def usingAsync[T](work: R => Future[T]): Future[T]
}
This has the advantage of not having to add the implicit arg every time you implement the method, but the disadvantage that I'm pure cargo culting - I don't understand why it works, though it seems to use similar principles to shapeless.
Given a made-up F type-class:
scala> trait F[A] {}
defined trait F
and this definition, which uses a context bound to require that the input A has a type-class instance of F:
scala> def f[A : F](x: A) = ???
f: [A](x: A)(implicit evidence$1: F[A])Nothing
I defined a Person and type-class instance:
scala> case class Person(name: String)
defined class Person
scala> implicit val person: F[Person] = new F[Person] {}
person: F[Person] = $anon$1#262b2c86
And the following compiles:
scala> f(Person("foo"))
scala.NotImplementedError: an implementation is missing
But, there's no String implementation, so it fails.
scala> f("foobar")
<console>:17: error: could not find implicit value for evidence parameter of type F[String]
f("foobar")
^
I then defined an F[String] using:
scala> implicit def fInstance(x: String) = new F[String] {}
fInstance: (x: String)F[String]
But, I can't run:
scala> f("foobar")
<console>:18: error: could not find implicit value for evidence parameter of type F[String]
f("foobar")
^
since I do not have an implicit F[String], but rather a String => F[String].
What's the proper way to use such an implicit def to meet the F[String] constraint, i.e. call the f function successfully with a type of String?
I got it to work via:
scala> implicit val x: F[String] = implicitly[String => F[String]].apply("foobar")
x: F[String] = $anon$1#7b7fdc8
scala> f("foobar")
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
at .f(<console>:12)
... 33 elided
But I'm not sure if it's the right/clean way to do it.
You defined an implicit conversion. If you want to use a def to provide typeclass instances you just write the same as you'd write an implicit val but replace val with def.
implicit def fInstance = new F[String] {}
Normally you only use a def if you need type parameters, like here.
implicit def fInstance[A] = new F[List[A]] {}
Or
implicit def fInstance[A](implicit ev: F[A]) = new F[List[A]] {}
Your fInstance defines an implicit conversion, i.e. a way to turn a String into F[String]. For generating a typeclass instance, a method accepting implicit parameters can be used:
implicit def fInstance(implicit x: String) = new F[String] {}
it is typically used in FP libraries to derive one typeclass from another:
implicit def optionMonoid[A](implicit S: Semigroup[A]): Monoid[Option[A]] = ???
// or, which is the same
// implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = ???
The idea is that F[String] can operate on any String in general, not being dependent on actual arguments provided into function. Of course, you can always provide instances explicitly:
f("foobar")(new F[String] { })
As a follow-up, the important part of typeclasses is that you can define them ad-hoc, i.e. not having access to definitions of F and String at all, and you are forced to scope implicits in Scala and import them, so it's totally ok.
Here is a simpler version of your definition (and you can remove implicit from fInstance):
implicit val singleFInstance: F[String] = fInstance("") // or fInstance("foobar"), etc.
Whether this is the right thing to do, very much depends on what F and f are supposed to mean.
But generally speaking: if F is really a type-class, fInstance(string) gives different results depending on the string (not just different instances, but different behavior), and f's signature is correct, then this is wrong and you should accept that calling f("foobar") isn't meaningful.