I'm trying to define, what I think to be a "default argument", with an implicit argument:
scala> def f(implicit x: Int, y: String) = y
f: (implicit x: Int, implicit y: String)String
Note - I tried to define implicit x: Int as the second argument, but I got a compile-time error:
scala> def f(y: String, implicit x: Int) = y
<console>:1: error: identifier expected but 'implicit' found.
def f(y: String, implicit x: Int) = y
Anyway, I define an implicit Int.
scala> implicit val x: Int = 100
x: Int = 100
Then I failed to invoke f with just a String argument:
scala> f("foo")
<console>:10: error: not enough arguments for method f:
(implicit x: Int, implicit y: String)String.
Unspecified value parameter y.
f("foo")
^
Next, I tried to invoke the f function by specifying the argument. That failed too.
scala> f(y = "foo")
<console>:10: error: not enough arguments for method f:
(implicit x: Int, implicit y: String)String.
Unspecified value parameter x.
f(y = "foo")
^
Is it possible to invoke the function f by providing an implicit and providing only a single argument for y?
Implicit parameter sections are all or nothing—either you explicitly pass every argument (assuming they don't have default values) or none of them. You need to do something like this if you want f("foo") to work:
def f(y: String)(implicit x: Int) = y
I'd be careful about this approach, though—I'm personally increasingly inclined to think that using implicits for anything except type class instances is a bad idea, and having an implicit Int value floating around is kind of scary.
Related
I would like to make the explicit constructor parameters of my class implicitly available in the class body.
I can mark the second and subsequent parameters as "implicit val", which works:
scala> class Foo(x: Int, implicit val y: String) {
| println(implicitly[String])
| }
defined class Foo
scala> new Foo(1,"hello")
hello
but if I mark the first parameter as implicit, then Scala thinks I am marking the whole argument list as an implicit argument list, and adds an empty first argument list:
scala> class Bar(implicit val x: Int, y: String) {
| println(implicitly[Int])
| }
defined class Bar
scala> new Bar(1,"hello")
<console>:9: error: too many arguments for constructor Bar: ()(implicit x: Int, implicit y: String)Bar
new Bar(1,"hello")
^
Is there any way to make the first explicit constructor argument explicitly in scope?
You can make it implicitly available by using an implicit def inside:
class Foo(val x: Int, implicit val y: String) {
implicit def ix = x
}
which is hideously verbose, but it doesn't look like there is another way to get around the ambiguity between implicit marking a parameter list implicit and implicit val marking an implicit field.
I would have said, Add other local modifiers:
scala> class Foo(#deprecated("","") private implicit val x: Int, val y: String) { println(implicitly[Int]) }
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined class Foo
scala> new Foo(42, "hi")
<console>:9: error: too many arguments for constructor Foo: ()(implicit x: Int, y: String)Foo
new Foo(42, "hi")
^
Syntactically, that's different from the leading implicit keyword, so maybe it's a parser bug, or maybe it does distinguish an implicit param list from a leading param marked locally-implicit but doesn't use the difference.
class Foo(x: Int, y: String) {
implicit val xImp = x
println(implicitly[Int])
}
Actually I would do it similarly for the first case too. This is a situation where I think that the lot more understandable code is worth a bit more verbosity:
class Foo(x: Int, y: String) {
implicit val yImp = y
println(implicitly[String])
}
I know view bounds may be deprecated soon. Please ignore that.
The following code compiles if only one of the last 3 implicit conversions are uncommented. Is this a compiler bug?
object Viewable extends App {
/** Speed of light in m/s */
val C: Double = 299293458d
/** #param weight in kilograms */
case class Matter(name: String, weight: Double) {
/** #return matter-energy equivalence in megajoules */
def energy: Double = weight * C * C / 1000000d
def megaJouleMsg: String = f"$name's mass-energy equivalence is $energy%.0f megajoules."
}
case class Animal(name: String, height: Double, weight: Double)
case class Vegetable(name: String, height: Double, weight: Double)
case class Mineral(name: String, weight: Double)
case class Bug(name: String, height: Double, weight: Double, canFly: Boolean)
case class Whale(name: String, height: Double, weight: Double, hasTeeth: Boolean)
case class AppleTree(name: String, height: Double, weight: Double, age: Int)
case class Grass(name: String, height: Double, weight: Double, edible: Boolean)
case class Sand(name: String, color: String, weight: Double)
case class Rock(name: String, color: String, weight: Double)
implicit def sandToMineral(sand: Sand) = Mineral(sand.name, sand.weight)
implicit def rockToMineral(rock: Rock) = Mineral(rock.name, rock.weight)
implicit def appleTreeToVegetable(tree: AppleTree) = Vegetable(tree.name, tree.height, tree.weight)
implicit def grassToVegetable(grass: Grass) = Vegetable(grass.name, grass.height, grass.weight)
implicit def bugToAnimal(bug: Bug) = Animal(bug.name, bug.height, bug.weight)
implicit def whaleToAnimal(whale: Whale) = Animal(whale.name, whale.height, whale.weight)
implicit def animalToMatter[X <% Animal](animal: X) = Matter(animal.name, animal.weight)
implicit def vegetableToMatter[X <% Vegetable](vegetable: X) = Matter(vegetable.name, vegetable.weight)
implicit def mineralToMatter[X <% Mineral](mineral: X) = Matter(mineral.name, mineral.weight)
println(Animal("Poodle", 1.0, 8.0).megaJouleMsg)
println(AppleTree("Spartan", 2.3, 26.2, 12).megaJouleMsg)
println(Rock("Quartz crystal", "white", 2.3).megaJouleMsg)
}
The error messages are:
type mismatch;
found : solutions.Viewable.Animal
required: ?{def megaJouleMsg: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method animalToMatter in object Viewable of type [X](animal: X)(implicit evidence$1: X => solutions.Viewable.Animal)solutions.Viewable.Matter
and method vegetableToMatter in object Viewable of type [X](vegetable: X)(implicit evidence$2: X => solutions.Viewable.Vegetable)solutions.Viewable.Matter
are possible conversion functions from solutions.Viewable.Animal to ?{def megaJouleMsg: ?}
println(Animal("Poodle", 1.0, 8.0).megaJouleMsg)
^
type mismatch;
found : solutions.Viewable.AppleTree
required: ?{def megaJouleMsg: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method animalToMatter in object Viewable of type [X](animal: X)(implicit evidence$1: X => solutions.Viewable.Animal)solutions.Viewable.Matter
and method vegetableToMatter in object Viewable of type [X](vegetable: X)(implicit evidence$2: X => solutions.Viewable.Vegetable)solutions.Viewable.Matter
are possible conversion functions from solutions.Viewable.AppleTree to ?{def megaJouleMsg: ?}
println(AppleTree("Spartan", 2.3, 26.2, 12).megaJouleMsg)
^
Well the compiler gives the error precisely. This is because there is more than one function which can Animal => Matter. i.e. your below 3 functions:
implicit def animalToMatter[X](animal: X) (implicit ev: X => Animal) = Matter(animal.name, animal.weight)
implicit def vegetableToMatter[X ](vegetable: X) (implicit ev: X => Vegetable) = Matter(vegetable.name, vegetable.weight)
implicit def mineralToMatter[X ](mineral: X) (implicit ev: X => Mineral) = Matter(mineral.name, mineral.weight)
are equally eligible to be called when you do Animal("Poodle", 1.0, 8.0).megaJouleMsg.
When you call megaJouleMsg the compiler looks for any implicit function available that can take Animal and return an object containing method megaJouleMsg (Matter in your case). Now all the 3 functions can take Animal (there is no constraint coded any where) and can return Matter. So the compiler gets confused on which function to calls
Solution: From what it appears, the view bound is not required. This will do:
implicit def animalToMatter(animal: Animal) = Matter(animal.name, animal.weight)
implicit def vegetableToMatter(vegetable: Vegetable) = Matter(vegetable.name, vegetable.weight)
implicit def mineralToMatter(mineral: Mineral) = Matter(mineral.name, mineral.weight)
scala> Animal("Poodle", 1.0, 8.0).megaJouleMsg
res1: String = Poodle's mass-energy equivalence is 716612592013 megajoules.
Edit: Looks like the confusion is because of X <% Animal. In animalToMatter function, it expects an implicit argument which can take X and return Animal. Now if you see in your scope there is no such function X => Animal. But because you are passing Animal("Poodle", 1.0, 8.0) it doesn't need any implicit function because it has already obtained Animal.
In short to repeat the compiler process on seeing Animal("Poodle", 1.0, 8.0).megaJouleMsg:
Sees all functions in the current scope which can take Animal and return Matter.
All the 3 functions can take Animal and return Matter. Note that vegetableToMatter and mineralToMatter will fail if accepted. i.e. though they are eligible but they will not succeed because they dont have any implicit function available that can X => Animal
Throws Error
As an example consider this:
scala> implicit def f[T <% Int](n:T) = Matter("",1)
warning: there were 1 feature warning(s); re-run with -feature for details
f: [T](n: T)(implicit evidence$1: T => Int)Matter
scala> 1.megaJouleMsg
res2: String = 's mass-energy equivalence is 89576574002 megajoules.
scala> "a".megaJouleMsg
<console>:12: error: No implicit view available from String => Int.
"a".megaJouleMsg
^
<console>:12: error: value megaJouleMsg is not a member of String
"a".megaJouleMsg
Notice the error? It gives:
No implicit view available from String => Int.
and not just value megaJouleMsg is not a member of String
Which simply means "a" was eligible for the implicit function f but f could not find a function that could convert it to Int and hence it gives error No implicit view available.... If it was not eligible for function f, then it would just throw error megaJouleMsg is not a member...
I define foo:
scala> def foo(x: Int, y:Int): Int = x + y
foo: (x: Int, y: Int)Int
Then I failed to set bar equal to the curried function of foo.
scala> def bar = foo.curried
<console>:8: error: missing arguments for method foo;
follow this method with `_' if you want to treat it as a partially applied
function
def bar = foo.curried
^
What am I doing wrong?
foo is not a function, it's a method. It's not an object, it has no own methods. curried is a method on object of type FunctionN.
You have to convert it to function:
(foo _).curried
With foo _ you are creating a new object of type Function2.
In a comment for SIP-13 Martin Odersky implied that it is possible to create an implicit method with multiple arguments. According to my experiences, implicit methods always have exactly one argument and I cannot imagine how an implicit method with multiple arguments can be used. Can someone give some use case and explanation?
For example if you need an implicit parameter of a function type:
implicit def foo(x: Int, y: Int) = y * x
def bar(x: Int, y: Int)(implicit f: (Int, Int) => Int) = f(x,y)
scala> bar(3,4)
res3: Int = 12
Could someone give a quick explanation why implicit conversion doesn't work in these cases? Thanks.
scala> implicit def strTwoInt (s: String):Int = s.toCharArray.map{_.asDigit}.sum
strTwoInt: (s: String)Int
scala> List[Int]("1","2","3") sum
res3: Int = 6
scala> List("1","2","3") sum
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
List("1","2","3") sum
scala> val a = List("1","2","3")
scala> a.foldLeft(0)((i:Int, j:Int) => i+j)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: (Int, java.lang.String) => Int
Your implicit conversion converts a String in an Int. In your first example, it is triggered by the fact that you try to put Strings into a List of Ints.
In your second example, you have a List of String and you call the method sum, which takes an implicit Numeric[String]. Your conversion does not apply because you neither try to pass a String somewhere the compiler was expecting an Int, nor you tried to call a method which is defined in Int and not in String. In that case, you can either define a Numeric[String] which use explicitly your conversion, or use a method which takes a List[Int] as parameter (giving hint to the compiler):
scala> def sumIt( xs: List[Int] ) = xs sum
sumIt: (xs: List[Int])Int
scala> sumIt( List("1","2","3") )
res5: Int = 6
In the third example, the foldLeft second argument must be of type:
(Int,String) => Int
the one you actually passed is of type:
(Int,Int) => Int
however, you did not define any implicit conversion between these two types. But:
a.foldLeft(0)((i:Int, j:String) => i+j)
triggers your conversion and works.
Edit: Here's how to implement the Numeric[String]:
implicit object StringNumeric extends math.Numeric[String] {
val num = math.Numeric.IntIsIntegral
def plus(x: String, y: String) = num.plus(x,y).toString
def minus(x: String, y: String) = num.minus(x,y).toString
def times(x: String, y: String) = num.times(x,y).toString
def negate(x: String): String = num.negate(x).toString
def fromInt(x: Int) = x.toString
def toInt(x: String) = x
def toLong(x: String) = toInt(x)
def toFloat(x: String) = toInt(x)
def toDouble(x: String) = toInt(x)
def compare(x:String,y:String) = num.compare(x,y)
}
scala> List("1","2","3") sum
res1: java.lang.String = 6
It works, but the result is a String.
Here's a quick explanation: implicit conversions apply to the types the convert from and to directly (e.g., String and Int in your case), and not to any parametrized type T[String] or T[Int] — unless implicit conversions have been defined for T itself, and it is not the case for lists.
Your implicit conversion does not apply in your two cases (and even if you had an implicit conversion from List[String] to List[Int], it wouldn't apply). It would automatically applied only when you need a value of type Int and you're passing String instead. Here, in the first case, the method sum asks for a Numeric[String] implicit parameter — an implicit conversion from String to Int does not come into play here.
Similar problem for your next attempt: the foldLeft on your collection requires a function of type (Int, String) => Int. Imagine the mess we would get into if, based on an implicit conversion from String to Int, the compiler automatically provided an implicit conversion from (Int, Int) => Int to (Int, String) => Int…
For all these cases, the easy way to fix it is to explicitly call .map(stringToInt) on your collection beforehand.