Conversions, into & Implicit Resolution
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Scala 3.10 (3.10.0, released 2026-10-12) and every example was compiled and run with Scala 3.10.0 through Scala CLI 1.17.1 on JDK 26 (checked on 2026-10-15). Where a feature depends on a release the page names it and says what the 3.9 LTS and 3.3 LTS lines do; those statements come from compiling the same code with 3.9.0 and 3.3.8 where the text says so, and from the release notes otherwise.
An implicit conversion lets the compiler adapt a value of one type to another type without the caller writing the call.
Implicit resolution is the search that finds the conversion, and also every given that a using clause needs. The two
share one set of lookup rules, so this page covers them together: first the conversions (the Conversion class, the language
import that guards them, and the into mechanism that scopes them to selected parameters), then the rules that decide which
given is found and which one wins, and finally how Scala 2 implicits map to Scala 3 and why conversions are used sparingly.
The examples use the trailhead scenario. The given and using syntax is introduced on
Givens & Using Clauses, extension methods (the preferred
alternative to many conversions) on Extension Methods, and the
overall design on Contextual Abstractions Overview.
Conversion[A, B]
A conversion is a given instance of scala.Conversion, an abstract class that is also a function:
abstract class Conversion[-T, +U] extends (T => U):
def apply(x: T): U
Source: Scala API: scala.Conversion
The compiler inserts a call to a conversion in these situations:
-
An expression
ehas typeTbut the expected typeSis somethingTdoes not conform to. The compiler looks for aConversion[T, S]and rewriteseasconversion.apply(e). -
In a selection
e.m, the type ofehas no memberm. The compiler looks for a conversion frometo a type that hasm. -
In an application
e.m(args), the type ofehas members namedmbut none applies toargs. The compiler looks for a conversion to a type with a member that does.
The first example defines a conversion in the companion of the target type and one in a separate object, and shows the first two situations:
//> using scala 3.10.0
package trailhead
import scala.language.implicitConversions
case class Trail(name: String, lengthKm: Double)
object Trail:
given fromName: Conversion[String, Trail] = Trail(_, 1.0)
object Shortcuts:
given lengthOfName: Conversion[String, Length] = Length(_)
class Length(val name: String):
def lengthKm: Double = name.length * 0.5
def describe(t: Trail): String = s"${t.name} (${t.lengthKm} km)"
@main def run(): Unit =
import Shortcuts.given
println(describe("Meadow")) // expected type Trail, found String
println("Meadow".lengthKm) // String has no member lengthKm
println(Trail.fromName("Ridge")) // calling the conversion explicitly
val t: Trail = "Pass"
println(t)
Meadow (1.0 km)
3.0
Trail(Ridge,1.0)
Trail(Pass,1.0)
Three points about where the conversions live:
-
fromNameneeds no import where aTrailis expected, because the companion object of the target type is part of the implicit scope (see below). -
lengthOfNameconvertsStringto a type with alengthKmmember. AStringdoes not mentionShortcutsanywhere, so the conversion must be imported withimport Shortcuts.given. Without it the compiler reports "value lengthKm is not a member of String, but could be made available as an extension method" and suggests the import that would fix it (checked with a version of this program that definedlengthKmonTrailand forgot the import). -
A conversion can be called by name, as
Trail.fromName("Ridge")shows, and becauseConversionextends the function typeT => Uit can also be passed where a function is expected.
An import of Shortcuts.* would not bring lengthOfName: wildcard imports leave out givens, and given selectors are
required (see Givens & Using Clauses).
The implicitConversions import and feature warnings
Conversions are the most abused part of Scala 2, so Scala 3 asks you to say that you want them. Using a given conversion
without the language feature enabled compiles but reports a feature warning at the place where the conversion is inserted
(the warning is shown with -feature; without it the compiler prints only "there was 1 feature warning"):
//> using scala 3.10.0
//> using option -feature
case class Trail(name: String, lengthKm: Double)
given Conversion[String, Trail] = Trail(_, 1.0)
@main def run(): Unit =
val t: Trail = "Pass"
println(t)
[warn] ./a2f.scala:9:18
[warn] Use of implicit conversion given instance given_Conversion_String_Trail should be enabled
[warn] by adding the import clause 'import scala.language.implicitConversions'
[warn] or by setting the compiler option -language:implicitConversions.
[warn] See the Scala docs for value scala.language.implicitConversions for a discussion
[warn] why the feature should be explicitly enabled.
[warn] val t: Trail = "Pass"
[warn] ^^^^^^
Trail(Pass,1.0)
The rules for the warning are:
-
A Scala 3 conversion, a
givenof typeConversion, warns where the compiler inserts it. Defining it does not warn. -
A Scala 2 style conversion, an
implicit defwith one parameter, warns where it is defined, and nothing warns at the call sites. The same file withimplicit def nameToTrail(name: String): Trailreports "Definition of implicit conversion method nameToTrail should be enabled" at the definition. -
The warning is silenced by
import scala.language.implicitConversionsin the scope of the definition or of the use, or by the compiler option-language:implicitConversionsfor the whole project. -
The reference says the feature warning is planned to become an error in a future Scala 3 version. Treat the import as mandatory.
The import is a deliberate opt-in per file. It also lets the compiler propagate expected types more widely in code that did not opt in, which improves type inference. The next section shows how to allow conversions in one place only.
into: scoped conversions (SIP-71)
A library often wants conversions for a few parameters, for example to accept an Int where it needs a Distance, without
forcing every user to import implicitConversions. The into mechanism (SIP-71) does that. It was a preview feature in 3.8,
is stable since Scala 3.9.0 and is therefore stable in 3.10; on the 3.3 LTS line it does not exist (the same code does not
compile with 3.3.8, which reports "value Conversion is not a member of scala" for import scala.Conversion.into).
There are two forms, into as a type constructor and into as a modifier.
into as a type constructor
into[T] is an opaque type, defined in the companion of Conversion, that marks a type as a conversion target. Where the
expected type is into[T], a conversion to T is inserted with no import and no warning:
//> using scala 3.10.0
//> using option -feature
package trailhead
import scala.Conversion.into
case class Distance(km: Double)
given Conversion[Int, Distance] = Distance(_)
def total(stages: into[Distance]*): Distance = Distance(stages.map(_.km).sum)
def plain(d: Distance): Distance = d
@main def run(): Unit =
println(total(3, Distance(2.5), 4)) // each argument converted on its own
println(plain(5)) // outside `into`: feature warning
[warn] ./i1.scala:18:17
[warn] Use of implicit conversion given instance given_Conversion_Int_Distance in package trailhead should be enabled
[warn] by adding the import clause 'import scala.language.implicitConversions'
[warn] or by setting the compiler option -language:implicitConversions.
[warn] See the Scala docs for value scala.language.implicitConversions for a discussion
[warn] why the feature should be explicitly enabled.
[warn] println(plain(5)) // outside `into`: feature warning
[warn] ^
Distance(9.5)
Distance(5.0)
The call to total converts only the Int arguments, one by one, because into applied to a vararg parameter converts each
argument on its own. The call to plain is outside any into and gets the feature warning. The same program compiles and
runs the same on 3.9.0.
into also works in other positions that the compiler treats as expected types: parameters of enum cases (so that a tree
type accepts Add(1, Neg(2)) where 1 is an Int converted to a Const), and the result of a function argument. The
second example shows both, plus what happens inside the method body: the compiler erases into from the types of parameters
as seen in the body, so a.km needs no unwrapping. Outside a body, .underlying of Conversion takes the wrapper off:
//> using scala 3.10.0
//> using option -feature
package trailhead
import scala.Conversion.into
case class Distance(km: Double)
given Conversion[Int, Distance] = Distance(_)
def longest(stages: List[Int], f: Int => into[Distance]): Distance =
stages.map(f).map(_.km).maxOption.fold(Distance(0))(Distance(_))
def sum(a: into[Distance], b: into[Distance]): Double =
a.km + b.km // inside the body the parameters are plain Distance
@main def run(): Unit =
println(longest(List(1, 2, 3), n => n * 2)) // lambda result converted
println(sum(3, Distance(1.5)))
val d: into[Distance] = Distance(2)
import Conversion.underlying
val plain: Distance = d.underlying
println(plain)
Distance(6.0)
4.5
Distance(2.0)
An enum shows the intended use for a tree or a small DSL: only the constructor parameters convert.
//> using scala 3.10.0
//> using option -feature
import scala.Conversion.into
enum Expr:
case Neg(e: into[Expr])
case Add(e1: into[Expr], e2: into[Expr])
case Const(n: Int)
import Expr.*
given Conversion[Int, Const] = Const(_)
def eval(e: Expr): Int = e match
case Const(n) => n
case Neg(e) => -eval(e)
case Add(a, b) => eval(a) + eval(b)
@main def run(): Unit =
println(eval(Add(1, Neg(2))))
-1
A type parameter instantiated to an into type also counts (List[into[Keyword]]("if", "then", "else") converts the
strings), but the conversion target is only recognised where the type is fully known. The reference warns that a value
declared as List[into[Keyword]] and built from List(ifKW, "then", "else") does not get the permission, and in 3.10 that
is reported as the ordinary feature warning for the two strings, not as an error (checked with -feature).
into as a modifier
If you own the target type, mark it instead of changing every signature. into is a soft modifier on classes, traits and
opaque type aliases, and conversions into the type then need no import wherever it is the expected type:
//> using scala 3.10.0
//> using option -feature
package trailhead
import scala.Conversion.into
into trait Waypoint
case class Summit(name: String) extends Waypoint
case class Hut(name: String) extends Waypoint
given Conversion[String, Summit] = Summit(_)
given Conversion[String, Waypoint] = Summit(_)
def visit(w: Waypoint): String = s"visit $w"
@main def run(): Unit =
println(visit("Matterhorn"))
visit Summit(Matterhorn)
This form is meant for a small set of designated conversion targets, such as a framework type used as a parameter type
everywhere. The reference states the limit plainly: keep the number of into types to the absolute minimum, and never make a
type into just in case someone might want to add a conversion later.
A valid conversion target type, the one that allows conversions without the import, is one of these: into[T]; a class,
trait or opaque type declared into, with any type arguments; a type alias, match type, annotated type or refined type
whose underlying type is valid; a union or intersection of two valid types; or a type parameter explicitly instantiated to a
valid type. The reference motivates the feature with the list concatenation xs ys` where `ys` is an array: the parameter of `
would be declared as into[IterableOnce[A]] so that the array converts without an import at the call site.
Source: Scala 3.9 release notes
Where the compiler looks for a given
Conversions and using arguments are found by the same search. Two scopes are consulted, in this order:
The lexical scope wins. The implicit scope is looked at only when the lexical scope has no candidate. The example uses a companion given and an imported one, and a wildcard import that does not bring givens:
//> using scala 3.10.0
package trailhead
class Units(val name: String)
object Units:
given fromCompanion: Units = Units("companion")
object Presets:
given metric: Units = Units("imported")
def name(using u: Units): String = u.name
@main def run(): Unit =
println(name) // only the implicit scope applies
locally:
import Presets.given
println(name) // the lexical scope wins
locally:
import Presets.*
println(name) // wildcard does not import givens
companion
imported
companion
Adding a local given local: Units next to import Presets.given in the same block makes the call ambiguous: "Ambiguous
given instances: both given instance metric in object Presets and given instance local match type trailhead.Units". Two
givens at the same nesting level are compared by the specificity and priority rules below, and when neither is better the
search fails.
The implicit scope of a type is built from its anchors: the companion objects of the class and of its parents, the
companions of its type arguments, and the objects on the path to the type (for p.o.Trail, the object o). A given in the
companion of Trail is therefore found anywhere a Trail is needed, without any import. This is why the companion is the
default home for the instances of a type class: see Type Classes.
Changes in implicit resolution
Scala 3 changed several rules of the Scala 2 search. They apply to given and to old-style implicit alike.
Nesting
A definition nested more deeply wins over an outer one, so the nearest given is picked instead of reporting an ambiguity. Scala 2 reports the ambiguity. The same program, one version in each language:
//> using scala 3.10.0
package trailhead
class Units(val name: String)
def outer(using a: Units): String =
def inner(using b: Units): String = summon[Units].name // b is nested deeper than a
inner(using Units("inner-parameter")) + " / " + summon[Units].name
def shadow(using a: Units): String =
given local: Units = Units("local-given")
summon[Units].name
@main def run(): Unit =
given global: Units = Units("global")
println(outer)
println(shadow)
inner-parameter / global
local-given
The same shape in Scala 2.13.18, with implicit parameters, fails:
[error] ./r1s2.scala:7:44
[error] ambiguous implicit values:
[error] both value a of type Units
[error] and value b of type Units
[error] match expected type Units
[error] def inner(implicit b: Units): String = implicitly[Units].name
[error] ^^^^^^^^^^^^^^^^^^
Package prefixes
A package prefix no longer contributes to the implicit scope of a type. A given defined at package level next to a type is
not in the implicit scope of that type from other packages, whereas a given in an object that the type lives in is. The two
files below define a type in an object o inside package p, with one given in o and one at the package level, and use it
from package q:
//> using scala 3.10.0
package p
trait Show[T]:
def show(t: T): String
object o:
class Trail(val name: String)
given inCompanionPath: Show[Trail] = t => s"o: ${t.name}"
given inPackage: Show[o.Trail] = t => s"p: ${t.name}"
package q
import p.{Show, o}
@main def run(): Unit =
val t = o.Trail("Ridge")
println(summon[Show[o.Trail]].show(t))
o: Ridge
Only the given in o is found. Put instances in the companion or in an object on the path of the type, or import them.
Ambiguity propagates
If an ambiguity is found while the compiler is resolving the arguments of a given, it is reported to the caller instead of
silently discarding that candidate (Scala 2 treated it as a plain failure of the candidate and went on, which people used to
simulate negation; use scala.util.NotGiven for that now). Here b needs an A, and there are two:
//> using scala 3.10.0
package trailhead
class A
class B
trait Show[T]:
def show: String
given a1: A = A()
given a2: A = A()
given b(using a: A): Show[B] = new Show[B] { def show = "b" }
@main def run(): Unit = println(summon[Show[B]].show)
[error] ./s2q.scala:13:48
[error] No best given instance of type trailhead.Show[trailhead.B] was found for parameter x of method summon in object Predef.
[error] I found:
[error]
[error] trailhead.b(
[error] /* ambiguous: both given instance a1 in package trailhead and given instance a2 in package trailhead match type trailhead.A */
[error] summon[trailhead.A]
[error] )
[error]
[error] But both given instance a1 in package trailhead and given instance a2 in package trailhead match type trailhead.A.
[error] println(summon[Show[B]].show)
[error] ^
Adding a plain given Show[B] next to it makes the program compile and print plain: it is then the only candidate that
does not depend on the ambiguous A. Divergence is also treated differently: a divergent candidate is a normal failure
after which other candidates are still tried.
Given prioritization (3.7)
When several givens match, Scala 3.0 to 3.6 picked the one with the most specific type. Scala 3.7 picks the most general one that satisfies the request. The reference sums it up with this example, in which the old rules reported an ambiguity:
//> using scala 3.10.0
package trailhead
class A
class B extends A
class C extends A
given A = A()
given B = B()
given C = C()
@main def run(): Unit =
println(summon[A].getClass.getSimpleName)
println(summon[B].getClass.getSimpleName)
A
B
The same file gives different answers by release (checked): 3.9.0 and 3.10.0 print A and B. With Scala 3.3.8 and with
-source:3.5 the first summon is rejected with "Ambiguous given instances: both given instance given_B … and given
instance given_C … match type trailhead.A". Compiling with -source:3.7-migration keeps the new behaviour and prints a
warning at each place where the choice changed ("Given search preference for trailhead.A between alternatives … has
changed. Previous choice: the first alternative. Choice from Scala 3.7: the second alternative.").
The consequence for library design: a given for a general type now shadows more specific instances that sit at the same
level, so a base-type fallback no longer makes the specific instances ambiguous. Other rules that changed in 3.0: a given
that takes using parameters is less specific than one that takes none, all else being equal; the old Scala 2 preference
for conversions with call-by-value over call-by-name parameters is gone; and the type of an implicit val or
implicit def has to be written explicitly (except in local blocks).
Source: Scala 3.7.0 release notes
Inaccessible companion givens (deprecated in 3.9, not found in 3.10)
Implicit search used to look into the companion of a type even when the call site could not refer to that companion, so a
private companion could still contribute givens. The specification has always required an eligible given to be accessible
at the point of the call. Scala 3.9 reports the use as a deprecation warning, and Scala 3.10 stops finding such instances:
//> using scala 3.10.0
package trailhead
object api:
class Config(val label: String)
private object Config:
given Config = Config("hidden default")
def describe(using c: Config): String = c.label
@main def run(): Unit = println(api.describe)
[error] ./h1.scala:11:45
[error] No given instance of type trailhead.api.Config was found for parameter c of method describe in object api
[error]
[error] The following import might fix the problem:
[error]
[error] import trailhead.api.Config.given_Config
[error]
[error] @main def run(): Unit = println(api.describe)
[error] ^
With 3.9.0 and -deprecation the same file compiles and prints hidden default, and the compiler says: "Usage of implicit
given instance given_Config defined in object Config, which is not accessible here. In Scala 3.10, this implicit will no
longer be found." Without -deprecation 3.9.0 reports only "there was 1 deprecation warning". With 3.3.8 the file compiles
and prints hidden default. The fix, for a library, is to make the companion public or to move the given to a public object.
The release notes say that about 20 projects in the community build stopped compiling on 3.10 for this reason, and that a
user often cannot fix it in their own code because the instance lives in a library.
Source: Scala 3.10 release notes
Passing explicit arguments to implicit parameters (3.7 warning)
Scala 3 writes explicit arguments for a context parameter with using, so that it is never confused with an argument to the
apply of the result. For a Scala 2 style implicit parameter, omitting using is allowed but, since 3.7, warns:
//> using scala 3.10.0
package trailhead
case class Config(label: String)
def withImplicit(implicit c: Config): String = c.label
def withUsing(using c: Config): String = c.label
@main def run(): Unit =
println(withImplicit(Config("explicit")))
println(withImplicit(using Config("explicit using")))
println(withUsing(using Config("given")))
[warn] ./x1.scala:11:24
[warn] Implicit parameters should be provided with a `using` clause.
[warn] This code can be rewritten automatically under -rewrite -source 3.7-migration.
[warn] To disable the warning, please use the following option:
[warn] "-Wconf:msg=Implicit parameters should be provided with a `using` clause:s"
[warn] println(withImplicit(Config("explicit")))
[warn] ^^^^^^^^^^^^^^^^^^
explicit
explicit using
given
For a real using clause the plain form is an error: withUsing(Config("explicit")) does not mean "pass this argument" but
"no given for parameter c", and the compiler reports "No given instance of type Config was found for parameter c of method
withUsing". Run with 3.3.8 the first line compiles without a warning. The rewrite -rewrite -source 3.7-migration inserts the
using keyword for you.
Source: Scala 3.7.0 release notes
Simulating Scala 2 implicits in Scala 3
Scala 3 still compiles the old implicit keyword (with the warnings above), and it keeps both forms in the common subset of
2.13 and 3. The reference maps each Scala 2 construct to a Scala 3 one:
| Scala 2 | Scala 3 | Notes |
|---|---|---|
|
|
|
|
|
A parameterised given maps to a class plus an implicit method in Scala 2 terms. Both the |
|
|
Explicit arguments are passed with |
|
|
The language import is still needed (see above). |
|
|
Other uses of implicit classes become a regular class plus a |
|
|
Unparameterised givens map to implicit objects. |
The program below mixes the Scala 2 forms (in Scala2Style) and their Scala 3 replacements (in Scala3Style), and compiles
with -feature -deprecation:
//> using scala 3.10.0
//> using option -feature
//> using option -deprecation
package trailhead
case class Trail(name: String, lengthKm: Double)
case class Config(label: String)
object Scala2Style:
implicit val defaultConfig: Config = Config("scala2")
implicit def nameToTrail(name: String): Trail = Trail(name, 1.0)
implicit class RichTrail(t: Trail):
def isLong: Boolean = t.lengthKm > 10
def label(implicit c: Config): String = implicitly[Config].label
object Scala3Style:
given Config = Config("scala3")
given Conversion[String, Trail] = Trail(_, 1.0)
extension (t: Trail) def isLong: Boolean = t.lengthKm > 10
def label(using Config): String = summon[Config].label
@main def run(): Unit =
import Scala2Style.*
println(label)
println(Trail("a", 12).isLong)
val t: Trail = "x"
println(t)
println(Scala3Style.label(using Config("explicit")))
[warn] ./s1.scala:12:16
[warn] Definition of implicit conversion method nameToTrail should be enabled
[warn] by adding the import clause 'import scala.language.implicitConversions'
[warn] or by setting the compiler option -language:implicitConversions.
[warn] See the Scala docs for value scala.language.implicitConversions for a discussion
[warn] why the feature should be explicitly enabled.
[warn] implicit def nameToTrail(name: String): Trail = Trail(name, 1.0)
[warn] ^
scala2
true
Trail(x,1.0)
explicit
The reference expected old-style implicits to be deprecated in a later version; on 3.10.0 they still compile (with the
warnings shown), and the compiler option -source:future previews their removal. Compiling the same file with -source:future rejects each Scala 2
form: “implicit` parameters are no longer supported, use a using clause instead", "`implicit` vals are no longer
supported, use a given clause instead", "`implicit` conversion methods are no longer supported. They can usually be replaced
by given instances of class Conversion” and "`implicit classes are no longer supported. They can usually be replaced by
extension methods". The -rewrite -source future-migration options turn each into the Scala 3 form. For projects that still
cross-compile with Scala 2.13 see Scala 2 & Scala 3 Compatibility
and Migrating from Scala 2.
The Scala 3.10 compiler also removes some conversions that user code relied on without knowing: runtime.RichInt and the
other Rich* implicit classes are gone, and Predef.ArrowAssoc is deprecated and no longer an implicit conversion. A library that
defined -> through its own implicit class can see Predef.-> chosen instead (release notes).
Source: Scala 3.10 release notes
Why conversions are discouraged
A conversion changes the meaning of the code at the place it is inserted, and the reader cannot see it there. The classic example is a lossy conversion:
//> using scala 3.10.0
//> using option -feature
import scala.language.implicitConversions
given Conversion[Double, Int] = _.toInt
def pages(n: Int): String = s"$n pages"
@main def run(): Unit =
println(pages(3.99))
val d: Double = 3.99
val n: Int = d
println(n)
3 pages
3
Scala would never narrow a Double to an Int silently, and one given quietly changes that for the whole file. The
problems are the same as in Scala 2:
-
The inserted call is invisible at the use site, so reading
pages(3.99)tells you nothing about the truncation. -
They can add up: two conversions in scope can make an expression typecheck in a way no one intended, or change which overload is picked.
-
They hide cost and failure: a conversion can allocate, parse or throw.
-
They weaken error messages and, by letting the compiler widen what it accepts, type inference.
-
Library authors cannot control where users enable them. That is the reason for
into.
The alternatives, in order of preference:
-
Extension methods for "this type should have more methods". The Scala 3 Book recommends them in place of Scala 2’s implicit classes: see Extension Methods.
-
Type classes for "this operation works for many types": see Type Classes.
-
Overloading or default arguments for a few argument shapes.
-
Explicit named conversions,
toXorfromXmethods orapplyin the companion. -
intowhen conversion on the argument really is the API, as in thexs ++ ysexample of the reference.
The one pattern for which conversions are still the idiomatic tool is the magnet pattern: a method takes a "magnet" type into
which several argument types convert, instead of overloads that would explode combinatorially. With into trait or into[...]
it needs no import for the callers; this version uses the language import for clarity:
//> using scala 3.10.0
import scala.language.implicitConversions
enum Arg:
case Name(text: String)
case Count(n: Int)
case Flag(on: Boolean)
object Arg:
given Conversion[String, Arg] = Name(_)
given Conversion[Int, Arg] = Count(_)
given Conversion[Boolean, Arg] = Flag(_)
def render(args: Arg*): String = args.map {
case Arg.Name(t) => s"name:$t"
case Arg.Count(n) => s"count:$n"
case Arg.Flag(on) => s"flag:$on"
}.mkString(", ")
@main def run(): Unit = println(render("ridge", 3, true))
name:ridge, count:3, flag:true
Conversions can also be used inside an instance, as a way to derive one type class instance from another through a view:
//> using scala 3.10.0
import scala.language.implicitConversions
class A(val x: Int)
given Conversion[A, Int] = _.x
given [T, S](using conv: Conversion[T, S], ordS: Ordering[S]): Ordering[T] = (x, y) => ordS.compare(conv(x), conv(y))
@main def run(): Unit = println(List(A(3), A(1), A(2)).sorted.map(_.x))
List(1, 2, 3)
Java and Kotlin
-
Java has a fixed set of implicit conversions: primitive widening (
inttolong), boxing and unboxing, and a few reference conversions. A program cannot add its own. Scala has the same for its numeric types and boxing (the standard library definesInttojava.lang.Integeras a conversion), and lets you define more. -
Kotlin has no implicit conversions; numeric widening must be explicit (
toLong()), and the use cases of implicit classes are covered by extension functions. See Kotlin Extension Functions & Scope Functions.
Where the books are outdated
-
Books and posts on Scala 2 present the implicit scope as including the package prefix, and describe an ambiguity between a nested and an outer implicit. Both differ in Scala 3.
-
Descriptions of "given prioritization" that say the most specific given wins describe Scala 3.0 to 3.6.
-
Books describe
implicit classas the way to add methods; useextension. -
Material before 3.8 says conversions always need
import scala.language.implicitConversions. Sinceinto(stable in 3.9) the import can be avoided for selected targets. -
Private companion givens were found until 3.9. Code and answers that rely on it do not compile on 3.10.
Scala 3.9 LTS versus 3.10
-
Conversionand the feature warning are the same on 3.3, 3.9 and 3.10. -
intois stable in 3.9 and 3.10 (checked with 3.9.0), preview in 3.8 and absent in 3.3 (checked with 3.3.8: noConversion.into, nointomodifier). -
The given prioritization of 3.7 applies on 3.9 and 3.10 (checked with 3.9.0) and not on 3.3 (3.3.8 reports the ambiguity).
-
Inaccessible companion givens are found with a deprecation warning on 3.9 (visible with
-deprecation) and not found on 3.10. Compile library code with 3.9 and-deprecationbefore moving to 3.10. -
The 3.7 warning for explicit arguments to
implicitparameters is on 3.9 and 3.10, not on 3.3.