Good example of an implicit parameter in Scala?

While the implicit parameters in Scala do not look very good for me - they are too close to global variables, however, since Scala seems to be a fairly strict language, I begin to doubt my own opinion :-).

Question: You could show a real (or close) good example when implicit parameters really work. IOW: something more serious than showPrompt , which would justify such a language design.

Or vice versa - can you demonstrate a reliable language design (maybe imaginary) that would not imply necessity. I think that even no mechanism is better than implicits, because the code is clearer and there are no assumptions.

Please note that I am asking about parameters, not implicit functions (transformations)!

Updates

Global variables

Thanks for the great answers. Perhaps I will clarify my objection to "global variables." Consider the following function:

 max(x : Int,y : Int) : Int 

you call it

 max(5,6); 

you can (!) do it like this:

 max(x:5,y:6); 

but in my eyes implicits works like this:

 x = 5; y = 6; max() 

it is not very different from such a construction (PHP-like)

 max() : Int { global x : Int; global y : Int; ... } 

Derek's answer

This is a great example, however, if you can think of the flexible use of sending a message without using implicit , send a counter example. I am really interested to know about cleanliness in language design; -).

+64
design scala parameters implicit
Mar 02 2018-12-12T00:
source share
8 answers

In a sense, yes, implicits represent a global state. However, they do not change, which is a real problem with global variables - you don’t see people complaining about global constants, right? In fact, coding standards usually specify that you convert any constants in your code to constants or enumerations, which are usually global.

Note also that implicits are not in a flat namespace, which is also a common problem with global ones. They are explicitly bound to types and, therefore, to the hierarchy of packages of these types.

So, take your global variables, make them immutable and initialized on the declaration site, and put them in namespaces. Do they still look global? Do they still look problematic?

But do not stop. Implicits are type-bound, and they are as "global" as types. Does the fact that types bother you globally?

As for the use cases, there are many of them, but we can do a brief review based on their history. Originally afaik, Scala had no symptoms. What is Scala, there were types of views, a feature of many other languages. We can still see that today, when you write something like T <% Ordered[T] , it means that type T can be considered as type Ordered[T] . View types is a way to automatically select parameters in type parameters (generics).

Scala then generalized this function with implicits. Automatic drops no longer exist, and instead you have implicit conversions that are simply Function1 values ​​and therefore can be passed as parameters. From now on, T <% Ordered[T] means that the value for the implicit conversion will be passed as a parameter. Since translation is automatic, the calling function does not need to explicitly pass a parameter, so these parameters become implicit parameters.

Note that there are two concepts - implicit conversions and implicit parameters - which are very close, but not completely overlapping.

In any case, views have become syntactic sugar for implicit conversions that are implicitly passed. They will be rewritten as follows:

 def max[T <% Ordered[T]](a: T, b: T): T = if (a < b) b else a def max[T](a: T, b: T)(implicit $ev1: Function1[T, Ordered[T]]): T = if ($ev1(a) < b) b else a 

Implicit parameters are just a generalization of this template, allowing you to pass any implicit parameters, not just Function1 . The actual use for them then followed, and the syntactic sugar for these uses last.

One of them is Context Bounds, which is used to implement a type type template (a template, since it is not a built-in function, but simply a way to use a language that provides similar functionality to a class like Haskell). A context constraint is used to provide an adapter that implements the functionality inherent in the class but not declared by it. It offers the benefits of inheritance and interfaces without their disadvantages. For example:

 def max[T](a: T, b: T)(implicit $ev1: Ordering[T]): T = if ($ev1.lt(a, b)) b else a // latter followed by the syntactic sugar def max[T: Ordering](a: T, b: T): T = if (implicitly[Ordering[T]].lt(a, b)) b else a 

You probably already used this - there’s one common precedent that people usually don’t notice. It:

 new Array[Int](size) 

Uses the context binding of the class manifest to enable such array initialization. We can see that in this example:

 def f[T](size: Int) = new Array[T](size) // won't compile! 

You can write it like this:

 def f[T: ClassManifest](size: Int) = new Array[T](size) 

In the standard library, the most used contextual boundaries are:

 Manifest // Provides reflection on a type ClassManifest // Provides reflection on a type after erasure Ordering // Total ordering of elements Numeric // Basic arithmetic of elements CanBuildFrom // Collection creation 

The last three are mainly used with collections with methods such as max , sum and map . One library that makes extensive use of context boundaries is Scalaz.

Another common application is to reduce the boiler plate during operations that must share a common parameter. For example, transactions:

 def withTransaction(f: Transaction => Unit) = { val txn = new Transaction try { f(txn); txn.commit() } catch { case ex => txn.rollback(); throw ex } } withTransaction { txn => op1(data)(txn) op2(data)(txn) op3(data)(txn) } 

Which is then simplified as follows:

 withTransaction { implicit txn => op1(data) op2(data) op3(data) } 

This pattern is used with transactional memory, and I think (but I'm not sure) that the Scala I / O library also uses it.

The third common use I can think of is to make evidence about passing types, which allows us to detect things during compilation that would otherwise lead to runtime exceptions. For example, see this definition in Option :

 def flatten[B](implicit ev: A <:< Option[B]): Option[B] 

This makes it possible:

 scala> Option(Option(2)).flatten // compiles res0: Option[Int] = Some(2) scala> Option(2).flatten // does not compile! <console>:8: error: Cannot prove that Int <:< Option[B]. Option(2).flatten // does not compile! ^ 

One library that makes extensive use of this feature is Shapeless.

I don’t think that the Akka library example fits into any of these four categories, but the whole point is universal: people can use it in different ways, rather than the ways prescribed by the language developer.

If you like it when prescribed for you (like Python), then Scala is just not for you.

+86
Mar 02 2018-12-12T00:
source share

Sure. Akka got a great example with respect to his Actors. When you are inside the Actor receive method, you can send a message to another player. When you do this, Akka will bind (by default) the current Actor as sender messages, for example:

 trait ScalaActorRef { this: ActorRef => ... def !(message: Any)(implicit sender: ActorRef = null): Unit ... } 

sender implicitly. The actor has a definition that looks like this:

 trait Actor { ... implicit val self = context.self ... } 

This creates an implicit value within your own code and allows you to do such simple things:

 someOtherActor ! SomeMessage 

Now you can do this if you want:

 someOtherActor.!(SomeMessage)(self) 

or

 someOtherActor.!(SomeMessage)(null) 

or

 someOtherActor.!(SomeMessage)(anotherActorAltogether) 

But usually this does not happen. You simply retain the natural use that is made possible by implicitly defining a value in the Actor trait. There are about a million other examples. Collectible classes are huge. Try wandering around any non-trivial Scala library and you will find a truck.

+21
Mar 02 2018-12-12T00:
source share

An example is the comparison operation on Traversable[A] . For example. max or sort :

 def max[B >: A](implicit cmp: Ordering[B]) : A 

This can only be reasonably determined by operation < on A Thus, without implication, wed must use the Ordering[B] context each time to use this function. (Or discard static type checking inside max and risk a startup error.)

If, however, an implicit comparison type class is in scope, for example. some Ordering[Int] , we can just use it right away or just change the comparison method by providing a different value for the implicit parameter.

Of course, implications can be obscured and, therefore, there may be situations in which the actual implicit, which is in scope, is not clear enough. For simple use of max or sort it may really be sufficient to have a fixed trait order on Int and use some syntax to check if this attribute is available. But this will mean that there can be no additional attributes, and each piece of code will have to use the properties that were originally defined.

Addition
The answer to comparing global variables.

I think you understood correctly that in code cut off as

 implicit val num = 2 implicit val item = "Orange" def shopping(implicit num: Int, item: String) = { "I'm buying "+num+" "+item+(if(num==1) "." else "s.") } scala> shopping res: java.lang.String = I'm buying 2 Oranges. 

it can smell like rotten and evil global variables. However, the crucial point is that there can only be one implicit variable for each type in the region. Your example with two Int will not work.

In addition, this means that practically implicit variables are used only when for the type there is not necessarily a unique, but a separate primary instance. The self actor reference is a good example for such a thing. A type example is another example. There may be dozens of algebraic comparisons for any type, but there are special ones. (At another level, the actual line number in the code itself may also contain a good implicit variable if it uses a very distinct type.)

Usually you do not use implicit for everyday types. And with specialized types (for example, Ordering[Int] ) there is not too much risk for shading them.

+8
Mar 02 2018-12-12T00:
source share

Another good general use of implicit parameters is that the type of the returned method depends on the type of some parameters passed to it. A good example mentioned by Jens is the structure of collections and methods of type map , whose full signature is:

 def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[GenSeq[A], B, That]): That 

Note that the return type is determined best by the CanBuildFrom that the compiler can find.

For another example, see the answer . There, the return type of the Arithmetic.apply method is determined according to the specific type of the implicit parameter ( BiConverter ).

+4
Mar 02 2018-12-12T00:
source share

It's easy, just remember:

  • declare a variable to be passed as implicit too
  • declare all implicit parameters after implicit parameters in a separate ()

eg.

 def myFunction(): Int = { implicit val y: Int = 33 implicit val z: Double = 3.3 functionWithImplicit("foo") // calls functionWithImplicit("foo")(y, z) } def functionWithImplicit(foo: String)(implicit x: Int, d: Double) = // blar blar 
+4
Oct 21 '13 at 17:35
source share

Based on my experience, there is no real good example of using implicits or implicits conversions.

The slight advantage of using implicits (no need to explicitly write a parameter or type) is redundant compared to the problems they create.

I have been a developer for 15 years and have been working with scala for the past 1.5 years.

I saw a lot of errors that were caused by the fact that the developer was not aware that implications are used, and that a certain function actually returns a different type specified by the specified one. Due to implicit conversion.

I have also heard statements that if you do not like the implications, do not use them. This is not practical in the real world, since in many cases external libraries are used, and many of them use implicits, so your code uses implicits, and you may not know about it. You can write code that has:

 import org.some.common.library.{TypeA, TypeB} 

or

 import org.some.common.library._ 

Both codes will be compiled and run. But they will not always get the same results, since the second version imports implicits conversion, which will lead to different code behaviors.

The “error” caused by this can happen very long after writing the code, if some of the values ​​affected by this conversion were not initially used.

Once you encounter an error, it is not an easy task to find the cause. You must conduct a thorough investigation.

Even if you feel like an expert in scala after you have found a mistake and corrected it by changing the import statement, you actually spent a lot of precious time.

Additional reasons why I am generally against non-appearance:

  • They make code harder to understand (less code, but you don't know what it does)
  • Compilation time. scala code compiles much more slowly when implicits are used.
  • In practice, it changes the language from statically typed to dynamically typed. It is true that by following very strict coding rules, you can avoid such situations, but in the real world this is not always the case. Even when using the IDE, “delete unused imports” may cause your code to still compile and run, but not match the previously deleted “unused” imports.

There is no way to compile scala without implicits (if there is, please correct me), and if there was an option, none of the shared libraries of the scala community would compile.

For all of the above reasons, I think implicits are one of the worst practices that scala uses.

Scala has many great features, and many of them are not so great.

When choosing a language for a new project, implicits is one of the reasons against scala, and not in favor of this. To my mind.

+4
Dec 02 '16 at 16:34
source share

Implicit parameters are heavily used in the collection API. Many functions receive an implicit CanBuildFrom, which ensures that you get the “best” implementation of the result set.

Without implications, you would either go through such a thing all the time that would make normal use cumbersome. Or use less specialized collections, which will be annoying because it will mean that you will lose performance / power.

+3
Mar 02 2018-12-12T00:
source share

I comment on this post a little late, but I started learning scala recently. Daniel and others gave a good background about the implicit keyword. I would give me two cents on an implicit variable from a practical point of view.

Scala is best suited for writing Apache Spark codes. In Spark, we have a spark context and, most likely, a configuration class that can extract configuration keys / values ​​from a configuration file.

Now, if I have an abstract class, and if I declare a configuration and spark context object as follows: -

 abstract class myImplicitClass { implicit val config = new myConfigClass() val conf = new SparkConf().setMaster().setAppName() implicit val sc = new SparkContext(conf) def overrideThisMethod(implicit sc: SparkContext, config: Config) : Unit } class MyClass extends myImplicitClass { override def overrideThisMethod(implicit sc: SparkContext, config: Config){ /*I can provide here n number of methods where I can pass the sc and config objects, what are implicit*/ def firstFn(firstParam: Int) (implicit sc: SparkContext, config: Config){ /*I can use "sc" and "config" as I wish: making rdd or getting data from cassandra, for eg*/ val myRdd = sc.parallelize(List("abc","123")) } def secondFn(firstParam: Int) (implicit sc: SparkContext, config: Config){ /*following are the ways we can use "sc" and "config" */ val keyspace = config.getString("keyspace") val tableName = config.getString("table") val hostName = config.getString("host") val userName = config.getString("username") val pswd = config.getString("password") implicit val cassandraConnectorObj = CassandraConnector(....) val cassandraRdd = sc.cassandraTable(keyspace, tableName) } } } 

As we can see the above code, I have two implicit objects in my abstract class, and I passed these two implicit variables as implicit parameters to the function / method / definition. I think this is the best use case that we can portray in terms of using implicit variables.

0
Jun 20 '17 at 6:38
source share



All Articles