Good example of implicit parameter in Scala? [closed]
Asked Answered
A

8

74

So far implicit parameters in Scala do not look good for me -- it is too close to global variables, however since Scala seems like rather strict language I start doubting in my own opinion :-).

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

Or contrary -- could you show reliable language design (can be imaginary) that would make implicit not neccessary. I think that even no mechanism is better than implicits because code is clearer and there is no guessing.

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

Updates

Global variables

Thank you for all great answers. Maybe I clarify my "global variables" objection. Consider such function:

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

you call it

max(5,6);

you could (!) 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 construct (PHP-like)

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

Derek's answer

This is great example, however if you can think of as flexible usage of sending message not using implicit please post an counter-example. I am really curious about purity in language design ;-).

Augury answered 2/3, 2012 at 9:32 Comment(2)
If you make a global implicit (and you can't - the best you can do is a package-scoped implicit) then your statement might hold true, but only if you chose to do such a thing... don't. And, the bottom line is that the flexibility of that API comes from the use of implicits. If you don't use them, you can't get the same flexibility. So you're asking to remove the feature that makes it great, and still make it great. Very odd request.Undeceive
@Derek Wyatt, the last comment is somewhat strange -- don't you seek optimization in life? I do. Now, about global variables -- I am not saying you have to have global variables to use implicits, I say they are similar in usage. Because they are binded by name of the callee, implicitly, and they are taken out of scope of caller, not from actual call.Augury
N
98

In a sense, yes, implicits represent global state. However, they are not mutable, which is the true problem with global variables -- you don't see people complaining about global constants, do you? In fact, coding standards usually dictate that you transform any constants in your code into constants or enums, which are usually global.

Note also that implicits are not in a flat namespace, which is also a common problem with globals. They are explicitly tied to types and, therefore, to the package hierarchy of those types.

So, take your globals, make them immutable and initialized at the declaration site, and put them on namespaces. Do they still look like globals? Do they still look problematic?

But let's not stop there. Implicits are tied to types, and they are just as much "global" as types are. Does the fact that types are global bother you?

As for use cases, they are many, but we can do a brief review based on their history. Originally, afaik, Scala did not have implicits. What Scala had were view types, a feature many other languages had. We can still see that today whenever you write something like T <% Ordered[T], which means the type T can be viewed as a type Ordered[T]. View types are a way of making automatic casts available on type parameters (generics).

Scala then generalized that feature with implicits. Automatic casts no longer exist, and, instead, you have implicit conversions -- which are just Function1 values and, therefore, can be passed as parameters. From then on, T <% Ordered[T] meant a value for an implicit conversion would be passed as parameter. Since the cast is automatic, the caller of the function is not required to explicitly pass the parameter -- so those parameters became implicit parameters.

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

Anyway, view types became syntactic sugar for implicit conversions being passed implicitly. They would be rewritten like this:

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

The implicit parameters are simply a generalization of that pattern, making it possible to pass any kind of implicit parameters, instead of just Function1. Actual use for them then followed, and syntactic sugar for those uses came latter.

One of them is Context Bounds, used to implement the type class pattern (pattern because it is not a built-in feature, just a way of using the language that provides similar functionality to Haskell's type class). A context bound is used to provide an adapter that implements functionality that is inherent in a class, but not declared by it. It offers the benefits of inheritance and interfaces without their drawbacks. 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 have probably used that already -- there's one common use case that people usually don't notice. It is this:

new Array[Int](size)

That uses a context bound of a class manifests, to enable such array initialization. We can see that with 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)

On the standard library, the context bounds most used 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 latter three are mostly used with collections, with methods such as max, sum and map. One library that makes extensive use of context bounds is Scalaz.

Another common usage is to decrease boiler-plate on 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 like this:

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 uses it as well.

The third common usage I can think of is making proofs about the types that are being passed, which makes it possible to detect at compile time things that would, otherwise, result in run time exceptions. For example, see this definition on Option:

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

That makes this 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 that feature is Shapeless.

I don't think the example of the Akka library fits in any of these four categories, but that's the whole point of generic features: people can use it in all sorts of way, instead of ways prescribed by the language designer.

If you like being prescribed to (like, say, Python does), then Scala is just not for you.

Nw answered 2/3, 2012 at 18:4 Comment(2)
That book you are writing should be definitively in English! :-) Thank you for great post.Augury
Why does SO not give a star option for answer like this? Really great post!Nonrigid
U
23

Sure. Akka's got a great example of it with respect to its Actors. When you're inside an Actor's receive method, you might want to send a message to another Actor. When you do this, Akka will bundle (by default) the current Actor as the sender of the message, like this:

trait ScalaActorRef { this: ActorRef =>
  ...

  def !(message: Any)(implicit sender: ActorRef = null): Unit

  ...
}

The sender is implicit. In the Actor there is a definition that looks like:

trait Actor {
  ...

  implicit val self = context.self

  ...
}

This creates the implicit value within the scope of your own code, and it allows you to do easy things like this:

someOtherActor ! SomeMessage

Now, you can do this as well, if you like:

someOtherActor.!(SomeMessage)(self)

or

someOtherActor.!(SomeMessage)(null)

or

someOtherActor.!(SomeMessage)(anotherActorAltogether)

But normally you don't. You just keep the natural usage that's made possible by the implicit value definition in the Actor trait. There are about a million other examples. The collection classes are a huge one. Try wandering around any non-trivial Scala library and you'll find a truckload.

Undeceive answered 2/3, 2012 at 9:52 Comment(13)
I think this is an even better example than Traversable.max type classes and the like.Marinelli
This is a good example. To some degree I think implicit variables are a way to AVOID global variables and "god singletons" (in lack of a better word) but still keep your code more readable as you don't have to explicitly pass some basic plumbing (the above mentioned singletons). And then again you still can pass them explicitly in for example while testing. So I think in many cases they allow for looser coupling and cleaner code.Shopwindow
@vertti, not exactly. I think the way C++ works is better here -- i.e. parameters per entire class and/or default arguments. For me idea that function sucks in an argument taken from somewhere by itself is very strange.Augury
@Derek Wyatt, ok, but wouldn't be better to have overloaded operator !? It would call ! with self in return. Please note, I am discussing language design, not something we already have.Augury
@macias You're missing the structure. The ! method is on the ScalaActorRef (i.e. it's a method on someOtherActor, which is an ActorRef, not an Actor, and it's definitely not 'this'). Because of this, the value that is 'self' must be passed in to the ! method. When you call ! on that ScalaActorRef you must pass in 'self', period.Undeceive
Ah, ok, I will mark this as solution and think of this exactly example, how to design language to avoid such need as implicit (not an irony, it has to be more pure way). Thank you very much!Augury
@macias I don't think you really get it. For example, you're saying that C++ "works better here". C++ can't do this. A default argument is not the same thing - at all. Since you're indicating that C++ has this functionality, you're telling me that you don't understand it yet. Don't code in Scala like you'd code in C++ - learn the language you're coding in and use its strengths.Undeceive
@Derek Wyatt, you are taking this too personally. "works better here" -- I hope I made myself clear what my metrics are, they are not the same as yours, so my "better" is not the same as your "better". You are glad you have implicits, I don't -- your example is great as mind-puzzle (and I am thankful for that) how to tackle the problem from another perspective. That's my POV, so please spare me the advices "learn the language you're coding in" (true, but not very polite -- patronizing is not welcome it any discussion).Augury
@macias This is going on way too long, but it's not personal at all. I'm patronizing you. You're confusing the feature - that's my point. You didn't understand it, which is why you asked this question - awesome. But you haven't quite grasped it yet (not surprising since this is a single example) and you're saying it's not useful for you. I'm saying, take time to learn it first. If you throw it away, you're not using the language for its strengths, in which case, I would suggest you don't use it at all.Undeceive
@Derek Wyatt, "you're saying it's not useful for you". I will be grateful for the verbatim quote. It is not PURE for me, don't you really see the difference? And I asked not for explanation how it works, but for examples for justifying its existence.Augury
Hmm, to see whether it’s pure, I think it would be useful to look at it historically. Surely, implicit parameters were not present in the original spec (at least the reference says they were introduced in 2.0), allegedly they were added not primarily for their purity (and probably neither for actors?) but to solve a problem in Scala pre-2.0. Maybe the reasoning back then would be enlightening for the question on why the feature is actually there.Marinelli
@Debilski, this too. I will think in two ways -- how to support the level of flexibility without introducing implicit, and second how to make implicit syntax more "explicit" ;-) For the second I have in mind something like with function.argument = value to make the binding to argument and avoid repetition of passing argument.Augury
@macias Oh crap! I'm really sorry, but my comment way up there should have read... I'm NOT patronizing you... I'm really not. Ugh... sorry about that.Undeceive
M
9

One example would be the comparison operations on Traversable[A]. E.g. max or sort:

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

These can only be sensibly defined when there is an operation < on A. So, without implicits we’d have to supply the context Ordering[B] every time we’d like to use this function. (Or give up type static checking inside max and risk a runtime cast error.)

If however, an implicit comparison type class is in scope, e.g. some Ordering[Int], we can just use it right away or simply change the comparison method by supplying some other value for the implicit parameter.

Of course, implicits may be shadowed and thus there may be situations in which the actual implicit which is in scope is not clear enough. For simple uses of max or sort it might indeed be sufficient to have a fixed ordering trait on Int and use some syntax to check whether this trait is available. But this would mean that there could be no add-on traits and every piece of code would have to use the traits which were originally defined.

Addition:
Response to the global variable comparison.

I think you’re correct that in a code snipped like

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 may smell of rotten and evil global variables. The crucial point, however, is that there may be only one implicit variable per type in scope. Your example with two Ints is not going to work.

Also, this means that practically, implicit variables are employed only when there is a not necessarily unique yet distinct primary instance for a type. The self reference of an actor is a good example for such a thing. The type class example is another example. There may be dozens of algebraic comparisons for any type but there is one which is special. (On another level, the actual line number in the code itself might also make for a good implicit variable as long as it uses a very distinctive type.)

You normally don’t use implicits for everyday types. And with specialised types (like Ordering[Int]) there is not too much risk in shadowing them.

Marinelli answered 2/3, 2012 at 10:25 Comment(5)
Thank you, however this is actually counterexample -- this should be a "trait" of the collection instance. And then you could use max() which would use ordering of the collection or max(comparer) which would use custom one.Augury
Sure, this would be possible. But it would also mean that one could not add another trait to, e.g. Int or any other pre-defined type whenever needed. (An oft-cited example is that of a semi group which might not be an original trait on Int, nor on String – and neither would it be possible to add this trait in a fixed form.) The problem is: There is no way to generalise a type over all possible traits. These is always code (type annotations) which have to be given ad-hoc or you lose type-safety. Implicit variables just reduce the boilerplate code for this.Marinelli
Not Int's given collection of Int's, like List or Array. If you assume the elements are comparable and you write such implicit as above you could as well, define the order at the top of the class (like in C++). In C++ the namespace is not polluted with arbitrary names like "cmp" here, because you pass the value.Augury
Thank you for that addition, I cannot upvote you more, sorry :-)Augury
If you are using Haskell terminology you may as well use it correctly. A value of type Ordering[Int] is a type class instance, not a type class.Barracoon
B
6

Based on my experience there is no real good example for use of implicits parameters or implicits conversion.

The small benefit of using implicits (not needing to explicitly write a parameter or a type) is redundant in compare to the problems they create.

I am a developer for 15 years, and have been working with scala for the last 1.5 years.

I have seen many times bugs that were caused by the developer not aware of the fact that implicits are used, and that a specific function actually return a different type that the one specified. Due to implicit conversion.

I also heard statements saying that if you don't like implicits, don't use them. This is not practical in the real world since many times external libraries are used, and a lot of them are using implicits, so your code using implicits, and you might not be aware of that. You can write a code that has either:

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

or:

import org.some.common.library._

Both codes will compile and run. But they will not always produce the same results since the second version imports implicits conversion that will make the code behave differently.

The 'bug' that is caused by this can occur a very long time after the code was written, in case some values that are affected by this conversion were not used originally.

Once you encounter the bug, its not an easy task finding the cause. You have to do some deep investigation.

Even though you feel like an expert in scala once you have found the bug, and fixed it by changing an import statement, you actually wasted a lot of precious time.

Additional reasons why I generally against implicits are:

  • They make the code hard to understand (there is less code, but you don't know what he is doing)
  • Compilation time. scala code compiles much slower when implicits are used.
  • In practice, it changes the language from statically typed, to dynamically typed. Its true that once following very strict coding guidelines you can avoid such situations, but in real world, its not always the case. Even using the IDE 'remove unused imports', can cause your code to still compile and run, but not the same as before you removed 'unused' imports.

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

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

Scala has many great features, and many not so great.

When choosing a language for a new project, implicits are one of the reasons against scala, not in favour of it. In my opinion.

Boeke answered 2/12, 2016 at 16:34 Comment(1)
It is worth to note that Kotlin got rid of implicits: kotlinlang.org/docs/reference/comparison-to-scala.htmlFondle
C
4

Another good general usage of implicit parameters is to make the return type of a method depend on the type of some of the parameters passed to it. A good example, mentioned by Jens, is the collections framework, and methods like map, whose full signature usually is:

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

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

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

Cover answered 2/3, 2012 at 10:22 Comment(4)
Maybe I miss something. You cannot guess here for type That, so you have to specify it, right? Wouldn't it be the same, if you omit type That, and simply convert the result by hand: map(it => it.foo).toBar() instead of map[B,List[Bars]](it => it.foo) ?Augury
@macias: The latter one doesn't create an intermediate collection. When you call toBar explicitly, first a Foo must created which is then converted to a Bar. When there is a type parameter a Bar can created directly.Cromlech
@macias: If you convert it by hand, you’re doing it in a second step, afterwards. You may get a List in return and then need to traverse it again just to get a Set. By using the implicit ‘annotation’, it is possible for the map method to avoid initialising and populating the wrong collection in the first place.Marinelli
@macias: you don't have to spell out the type parameters to the map method - they can be inferred. val lf: List[Foo] = …; val sb: Set[Bar] = lf map (_.toBar) //no intermediate List[Bar]Steel
L
4

It's easy, just remember:

  • to declare the variable to be passed in as implicit too
  • to declare all the implicit params after the non-implicit params in a separate ()

e.g.

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
Language answered 21/10, 2013 at 17:35 Comment(0)
Z
3

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

Without implicits you would either pass such a thing all the time, which would make normal usage cumbersome. Or use less specialized collections which would be annoying because it would mean you loose performance/power.

Zildjian answered 2/3, 2012 at 9:49 Comment(0)
I
0

I am commenting on this post a bit late, but I have started learning scala lately. Daniel and others have given nice background about implicit keyword. I would provide me two cents on implicit variable from practical usage perspective.

Scala is best suited if used for writing Apache Spark codes. In Spark, we do have spark context and most likely the configuration class that may fetch the configuration keys/values from a configuration file.

Now, If I have an abstract class and if I declare an object of configuration and spark context 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 e.g.*/
    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 code above, I have two implicit objects in my abstract class, and I have passed those two implicit variables as function/method/definition implicit parameters. I think this is the best use case that we can depict in terms of usage of implicit variables.

Izard answered 20/6, 2017 at 6:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.