Taken from "Scala with cats" (page 18):
Implicit Conversions
When you create a type class instance constructor using an implicit def, be sure to mark the parameters to the method as implicit parameters. Without this keyword, the compiler won’t be able to fill in the parameters during implicit resolution. implicit methods with non‐implicit parameters form a different Scala pattern called an implicit conversion. This is also different from the previous section on Interface Syntax, because in that case the JsonWriter is an implicit class with extension methods. Implicit conversion is an older programming pattern that is frowned upon in modern Scala code. Fortunately, the compiler will warn you when you do this. You have to manually enable implicit conversions by importing scala.language.implicitConversions in your file
Can anyone please sum up well why implicit conversion are deprecated? What were there limit or issues? Why the approach with implicit parameters is better?
Note I know how to use the modern approach well, including chaining implicit and all. I am just curious as to what was the issue and why it was deprecated.
Martin Odersky, the inventor of Scala, has indicated that implicits (and implicit conversions) are being deprecated in scala 3.1 and will eventually be removed from the language altogether.
the implicit functionality will be replaced with Extension Methods and Givens. Extension Methods and Givens provide a more tighter functional solution that doesn't introduce the unsuspecting and hidden side effects that implicits cause. Odersky now views implicits as a “recipe for disaster" and are "too implicit" which was his motivation to replace their functionality in 3.x.
https://www.slideshare.net/Lightbend/scala-3-is-coming-martin-odersky-shares-what-to-know
https://hub.packtpub.com/is-scala-3-0-a-new-language-all-together-martin-odersky-its-designer-says-yes-and-no/
Related
Considering a simple use case to extract fields of an object in Java reflection:
val fields = this.getClass.getDeclaredFields
it may return incomplete information if generic type is used, but it is short, fast and can handle most cases.
In Scala reflection module (scala.reflect), it appears that such one-liner doesn't exist, the shortest code I know of that can successfully handle this case is:
trait Thing[T <: Thing[T]{
implicit ev: TypeTag[T]
scala.reflect.runtime.universe.typeOf[T].members
}
This uses a redundant F-bounded polymorphism and a TypeTag doesn't carry extra information comparing to a Java class, is there any way to make it shorter, at least as short as Java (which is a very verbose language)?
Thanks a lot for your advice
I'm not sure that in this specific case
this.getClass.getDeclaredFields
is much shorter than
def fields[T: TypeTag](t: T) = typeOf[T].decls
fields(this)
Anyway you can still use Java reflection in Scala.
Sometimes Scala reflection is more verbose than Java reflection but Scala reflection allows to do things (in Scala terms) that can't be done with Java reflection (for example if corresponding Scala concepts are absent in Java).
It's not true that
TypeTag doesn't carry extra information comparing to a Java class
Types and classes are different concepts. Maybe you meant ClassTags.
To use or not to use F-bounded polymorphism is your choice.
Why REPL gives warning on using structural types? Are structural types unsafe to use?
scala> def test(st: { def close():Unit})
| = st.close()
<console>:12: warning: reflective access of structural type member method close should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scaladoc for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
= st.close()
^
test: (st: AnyRef{def close(): Unit})Unit
The warning suggests viewing the Scaladoc page, which says
Why control it? Reflection is not available on all platforms. Popular
tools such as ProGuard have problems dealing with it. Even where
reflection is available, reflective dispatch can lead to surprising
performance degradations.
Source
To make the warning go away, simply add import scala.language.reflectiveCalls to the top of your file to indicate that, yes, you do in fact intend to use this language feature.
I would like to put another point of view than Silvio Mayolo's answer.
Fundamentally the important thing here is that Scala is compiled into a JVM rather than some Scala-specific target platform. This means that many features that exist in Scala are not supported out of the box by the target platform (JVM) and they have to be somehow simulated by the compiler. One of such features is structural types and it is implemented using reflection. And using reflection introduces a bunch of potential issues that are mentioned in Silvio's answer. Thus compiler developers want to make sure that you understand possible drawbacks and still want to use this feature by enforcing either explicit import, compiler configuration or warning.
As for alternatives in your case you may use java.lang.AutoCloseable instead of your structural type and it will probably cover most of your cases.
Another alternative is to use implicit parameter and type class idea:
trait CloseableTC[A] {
def close(closeable: A): Unit
}
object CloseableTC {
implicit val javaCloseable: CloseableTC[java.lang.AutoCloseable] = new CloseableTC[java.lang.AutoCloseable] {
override def close(closeable: AutoCloseable): Unit = closeable.close()
}
// add here more implicits for other classes with .close()
}
def testTC[A](a: A)(implicit closeableTC: CloseableTC[A]) = closeableTC.close(a)
import CloseableTC._
Shapeless has a neat type class derivation mechanism that allows you to define typeclasses and get automatic derivation for any typeclass.
To use the derivation mechanism as a user of a typeclass, you would use the following syntax
import MyTypeClass.auto._
which as far as I understand it is equivalent to
import MyTypeClass.auto.derive
An issue arises when you try and use multiple typeclasses like such within the same scope. It would appear that the Scala compiler only considers the last definition of derive even though there are two versions of the function "overloaded" on their implicit arguments.
There are a couple ways I can think of to fix this. Instead of listing them here, I will mark them as answers that you can vote on to confirm sanity as well as propose any better solution.
I raised this question back in April and proposed two solutions: defining the method yourself (as you suggest):
object AutoCodecJson {
implicit def deriveEnc[T] = macro deriveProductInstance[EncodeJson, T]
implicit def deriveDec[T] = macro deriveProductInstance[DecodeJson, T]
}
Or using aliasing imports:
import AutoEncodeJson.auto.{ derive => deriveEnc }
import AutoDecodeJson.auto.{ derive => deriveDec }
I'd strongly suggest going with aliasing imports—Miles himself said "hadn't anticipated that macro being reused that way: not sure I approve" about the deriveProductInstance approach.
Instead of inheriting from the Companion trait, define the auto object and apply method yourself within your companion object and name them distinctively. A possible drawback to this is that two separate librairies using shapeless could end up defining a derive method with the same name and the user would end up again with a situation where he cannot use the derivation process for both type classes within the same scope in his project.
Another possible drawback is that by dealing with the macro call yourself, you may be more sensitive to shapeless API changes.
Modify/fix the Scala compiler to accept two different methods overloaded on their implicit parameters.
Is there any reason why this is impossible in theory?
In the Scala reflection guide is written the following:
As with Manifests, one can in effect request that the compiler
generate a TypeTag. This is done by simply specifying an implicit
evidence parameter of type TypeTag[T]. If the compiler fails to find a
matching implicit value during implicit search, it will automatically
generate a TypeTag[T].
This StackOverflow answer beautifully explains the concept of "implicit evidence". However, it is still not completely clear to me what it means that the compiler will
generate a TypeTag[T].
Does this mean that this is a special case of "implicit evidence" search? I.e. the class TypeTag[T] is handled in a special way when the compiler does implicit search ? I tried to look for implicit parameter values in the Scala reflection APIs but I did not find any which provides a TypeTag[T], so I assume the TypeTag[T] implicit parameter is coming from inside the compiler (as the documentation says). So the classname TypeTag[T] is hardcoded into the compiler's source. Is this assumption correct ?
Is the automatic generation of implicit values documented somewhere? In other words, is there a documentation somewhere which lists all the automatically generated implicit evidences ? I did not find TypeTag[T] in the Scala language specification (version 2.9). The closest concept there to TypeTag[T] is Manifest which are automatically generated implicit parameters. Are Manifests the only automatically generated implicit value parameters in Scala 2.9 ?
Yes, TypeTags and WeakTypeTags are treated specially by implicit search. Now that implicit macros actually work, we plan to remove this hardcode, but that remains to be implemented.
So far there's no documentation for automatic generation of implicit values apart from source code, which says that only type tags and manifests are currently generated: https://github.com/scala/scala/blob/38ee986bcad30a8835e8f197112afb5cce2b76c5/src/compiler/scala/tools/nsc/typechecker/Implicits.scala#L1288
The following Scala code works correctly:
val str1 = "hallo"
val str2 = "huhu"
val zipped: IndexedSeq[(Char, Char)] = str1.zip(str2)
However if I import the implicit method
implicit def stringToNode(str: String): xml.Node = new xml.Text(str)
then the Scala (2.10) compiler shows an error: value zip is not a member of String
It seems that the presence of stringToNode somehow blocks the implicit conversion of str1 and str2 to WrappedString. Why? And is there a way to modify stringToNode such that zip works but stringToNode is still used when I call a function that requires a Node argument with a String?
You have ambiguous implicits here. Both StringOps and xml.Node have the zip-method, therefore the implicit conversion is ambiguous and cannot be resolved. I don't know why it doesn't give a better error message.
Here are some links to back it up:
http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.StringOps
and
http://www.scala-lang.org/api/current/index.html#scala.xml.Node
edit: it was StringOps, not WrappedString, changed the links :) Have a look at Predef: http://www.scala-lang.org/api/current/index.html#scala.Predef$
to see predefined implicits in Scala.
I would avoid using implicits in this case. You want 2 different implicit conversions which both provide a method of the same name (zip). I don't think this is possible. Also, if you import xml.Text, you can convert with just Text(str) which should be concise enough for anyone. If you must have this implicit conversion to xml.Node, I would pack the implicit def into an object and then import it only in the places where you need it to make your code readable and to, possibly, avoid conflicts where you also need to zip strings. But basically, I would very much avoid using implicits just to make convenient conversions.
Like #Felix wrote, it is generally a bad idea to define implicit conversions between similar data types, like the one you used. Doing that weakens type system, leads to ambiguities like you encountered and may produce extremely unclear ("magic") code which is very hard to analyze and debug.
Implicit conversions in Scala are mostly used to define lightweight, short-lived wrappers in order to enrich API of wrapped type. Implicit conversion that converts String into WrappedString falls into that category.
Twitter's Effective Scala has a section about this issue.