How do Kotlin's extension functions work? - scala

Let's say I want an integer that supplies a square method.
Kotlin:
fun Int.square() = this * this
usage:
println("${20.square()}")
doc:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.
We would like to emphasize that extension functions are dispatched statically
My expectation would've been that they simply add it to the member functions of the extended class during compilation, but that is what they explicitly deny, so my next thought was it could be "sort of" like scala implicits.
Scala:
object IntExtensions{
implicit Class SquareableInt(i:Int){
def square = i*i
}
}
usage:
import IntExtensions._
and then
println(f"${20.square}")
doc:
An implicit class is desugared into a class and implicit method pairing, where the implciit method mimics the constructor of the class.
The generated implicit method will have the same name as the implicit class.
But scala implicits create a new class, that would disable the usage of this.
So ... how IS it that Kotlin extends classes? "Make callable" isn't telling me much.

In your case Kotlin just create simple utils-class with name "filename"Kt and static method "int square(int x)" (java pseudo-code)
From Java it look something like this
// filename int-utils.kt
final class IntUtilsKt {
public static int square(int x) {
return x * x;
}
}
And after this all calls to
val result = 20.square()
will be transformed (on byte-code level) to
val result = IntUtilsKt.square(20);
P.S.
You can see it yourself using IDEA action "Show Kotlin byte-code"

Related

Scala Class that containing List

I have a very basic and simple Scala question. For example, I have a java class like that
class Dataset{
private List<Record> records;
Dataset(){
records = new ArrayList<Record>()
}
public void addItem(Record r){
records.add(r)
}
}
When I try to write same class in Scala, I encoutered with some error:
class RecordSet() {
private var dataset:List[Record]
def this(){
dataset = new List[Record]
}
def addRecord(rd: Record)={
dataset :+ rd
}
}
I cannot declare a List variable like ( private var dataset:List[Record])
and cannot write a default constructor.
Here is how you will replicate the Java code you mentioned in your question:
// defining Record so the code below compiles
case class Record()
// Here is the Scala implementation
class RecordSet(private var dataset:List[Record]) {
def addRecord(rd: Record)={
dataset :+ rd
}
}
Some explanation:
In Scala, when you define a class, you have the ability to pass parameter to the class definition. eg: class Foo(num:Int, descr:String) Scala would automatically use the given parameter to create a primary constructor for you. So you can now instantiate the Foo, like so new Foo(1, "One"). This is different in Java where you have to explicitly define parameter accepting constructors.
You have to be aware that the parameter passed do not automatically become instance member of the class. Although if you want, you can tell Scala to make them instance member. There are various ways to do this, one way is to prefix the parameter with either var or val. For example class Foo(val num:Int, val descr:String) or class Foo(var num:Int, var descr:String). The difference is that with val, the instance variable are immutable. With var they are mutable.
Also, by default the instance member Scala will generate would be public. That means they can be accessed directly from an instance of the object. For example:
val foo = new Foo(1, "One")
println(foo.num) // prints 1.
If you want them to be private, you add private keyword to the definition. So that would become:
class Foo(private var num:Int, private var desc:String)
The reason why your code fails to compile is you define a method called this() which is used to create multiple constructors. (and not to create a constructor that initiates a private field which is your intention judging from the Java code you shared). You can google for multiple constructors or auxiliary constructors to learn more about this.
As dade told the issue in your code is that with this keyword you are actually creating an auxilary constructor which has some limitations like the first line of your auxilary constructor must be another constructor (auxilary/primary). Hence you cannot use such a way to create a class.
Also you can not write such lines in a scala concrete class private var dataset:List[Record] as it is considered as abstract (no definition provided).
Now with the code. Usually in Scala we don't prefer mutability because it introduces side-effects in our functions (which is not the functional way but as scala is not purely functional you can use mutability too).
In Scala way, the code should be something like this:
class RecordSet(private val dataset:List[Record]) {
def addRecord(rd: Record): RecordSet ={
new RecordSet(dataset :+ rd)
}
}
Now with the above class there is no mutability. Whenever you are adding on an element to the dataset a new instance of RecordSet is being created. Hence no mutability.
However, if you have to use the same class reference in your application use your a mutable collection for your dataset like below:
class RecordSet(private val dataset:ListBuffer[Record]) {
def addRecord(rd: Record): ListBuffer[Record] ={
dataset += rd
}
}
Above code will append the new record in the existing dataset with the same class reference.

Understanding companion object in scala

While learning Scala, I came across interesting concept of companion object. Companion object can used to define static methods in Scala. Need few clarifications in the below Spark Scala code in regard of companion object.
class BballStatCounter extends Serializable {
val stats: StatCounter = new StatCounter()
var missing: Long = 0
def add(x: Double): BballStatCounter = {
if (x.isNaN) {
missing += 1
} else {
stats.merge(x)
}
this
}
}
object BballStatCounter extends Serializable {
def apply(x: Double) = new BballStatCounter().add(x)
}
Above code is invoked using val stat3 = stats1.map(b=>BballStatCounter(b)).
What is nature of variables stats and missing declared in the
class? Is it similar to class attributes of Python?
What is the significance of apply method in here?
Here stats and missing are class attributes and each instance of BballStatCounter will have their own copy of them just like in Python.
In Scala the method apply serves a special purpose, if any object has a method apply and if that object is used as function calling notation like Obj() then the compiler replaces that with its apply method calling, like Obj.apply() .
The apply method is generally used as a constructor in a Class Companion object.
All the collection Classes in Scala has a Companion Object with apply method, thus you are able to create a list like : List(1,2,3,4)
Thus in your above code BballStatCounter(b) will get compiled to BballStatCounter.apply(b)
stats and missing are members of the class BcStatCounter. stats is a val so it cannot be changed once it has been defined. missing is a var so it is more like a traditional variable and can be updated, as it is in the add method. Every instance of BcStatCounter will have these members. (Unlike Python, you can't add or remove members from a Scala object)
The apply method is a shortcut that makes objects look like functions. If you have an object x with an apply method, you write x(...) and the compiler will automatically convert this to x.apply(...). In this case it means that you can call BballStatCounter(1.0) and this will call the apply method on the BballStatCounter object.
Neither of these questions is really about companion objects, this is just the normal Scala class framework.
Please note the remarks in the comments about asking multiple questions.

Reflecting superclass's value type arguments in Scala, without TypeTag

In Java, while type arguments are erased in runtime, it is possible to find the actual type arguments passed to a superclass:
class Derived extends Base<String> {
// ...
}
ParameterizedType type = (ParameterizedType)Derived.class.getGenericSuperclass();
Type[] args = type.getActualTypeArguments(); // gives {String.class}
While I can use the same Java reflection to Scala class, It does not catch Scala's value types:
class Base[T]
class Derived extends Base[Int]
classOf[Derived]
.getGenericSuperclass
.asInstanceOf[ParameterizedType]
.getActualTypeArguments // gives {Object.class}, not {int.class}
Is it possible to determine the value type used when extending from a generic superclass? I am loading classes from a jar file so it'd be best to achieve this only using a java.lang.Class instance.
In Java reflection you won't be able to obtain Int and other AnyVal types because they are handled specially by the compiler and if they are used generically, they will be represented by Object. However, you can use Scala reflection, and it is wholly possible to go from Java reflection to Scala reflection. Here's how:
import scala.reflect.runtime.universe._
class Base[T]
class Derived extends Base[Int]
object Main extends App {
val rm = runtimeMirror(getClass.getClassLoader) // whatever class loader you're using
val derivedSym = rm.staticClass(classOf[Derived].getName)
val baseSym = rm.staticClass(classOf[Base[_]].getName)
val TypeRef(_, _, params) = derivedSym.typeSignature.baseType(baseSym)
println(s"$derivedSym extends $baseSym[${params.mkString(", ")}]")
}
Unfortunately, unless you know exactly what you are searching for, you will have hard time finding proper documentation. I have found the answer on scala-users mailing list. Scala reflection is still experimental and, AFAIK, it will probably be superseded by a better one in future Scala versions.

Scala object that extends Java class

An old trick I used in my previous Java projects was to create e.g. a FileUtils class that offered helper functions for common file operations needed by my project and not covered by e.g. org.apache.commons.io.FileUtils. Therefore my custom FileUtils would extend org.apache.commons.io.FileUtils and offer all their functions as well.
Now I try to do the same in Scala but the apache helper functions are not seen through my FileUtils Scala object, what is wrong here?
import org.apache.commons.io.{ FileUtils => ApacheFileUtils }
object FileUtils extends ApacheFileUtils {
// ... additional helper methods
}
val content = FileUtils.readFileToString(new File("/tmp/whatever.txt"))
here the compiler complains that readFileToString is not a member of my Scala FileUtils but it is of ApacheFileUtils and I extend from it ...
The Scala equivalent of a class with static methods is an object, so in Scala terms, the static components of FileUtils are seen as
object FileUtils {
def readFile(s:String) = ???
...
}
And in Scala, you can't extend an object. This is illegal:
object A
object B extends A // A is not a type
Therefore object FileUtils extends ApacheFileUtils only gives you access to the class-level definitions of ApacheFileUtils (that except for the base Object methods like equals and hashCode, you have none)
You might find that Scala offers more elegant ways of providing extensions. Have a look at the 'pimp up my library' pattern for good starting point.
To apply this pattern to your example:
// definition of your "pimped" methods
import java.io.File
class RichFile(file:File) {
def readToString():String = ???
}
// companion object defines implicit conversion
object RichFile {
implicit def fileToRichFile(f:File):RichFile = new RichFile(f)
}
// Usage
import RichFile._
val content = new File("/tmp/whatever.txt").readToString

How can I add new methods to a library object?

I've got a class from a library (specifically, com.twitter.finagle.mdns.MDNSResolver). I'd like to extend the class (I want it to return a Future[Set], rather than a Try[Group]).
I know, of course, that I could sub-class it and add my method there. However, I'm trying to learn Scala as I go, and this seems like an opportunity to try something new.
The reason I think this might be possible is the behavior of JavaConverters. The following code:
class Test {
var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}
does not compile, because there is no asScala method on Java's ArrayList. But if I import some new definitions:
class Test {
import collection.JavaConverters._
var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}
then suddenly there is an asScala method. So that looks like the ArrayList class is being extended transparently.
Am I understanding the behavior of JavaConverters correctly? Can I (and should I) duplicate that methodology?
Scala supports something called implicit conversions. Look at the following:
val x: Int = 1
val y: String = x
The second assignment does not work, because String is expected, but Int is found. However, if you add the following into scope (just into scope, can come from anywhere), it works:
implicit def int2String(x: Int): String = "asdf"
Note that the name of the method does not matter.
So what usually is done, is called the pimp-my-library-pattern:
class BetterFoo(x: Foo) {
def coolMethod() = { ... }
}
implicit def foo2Better(x: Foo) = new BetterFoo(x)
That allows you to call coolMethod on Foo. This is used so often, that since Scala 2.10, you can write:
implicit class BetterFoo(x: Foo) {
def coolMethod() = { ... }
}
which does the same thing but is obviously shorter and nicer.
So you can do:
implicit class MyMDNSResolver(x: com.twitter.finagle.mdns.MDNSResolver) = {
def awesomeMethod = { ... }
}
And you'll be able to call awesomeMethod on any MDNSResolver, if MyMDNSResolver is in scope.
This is achieved using implicit conversions; this feature allows you to automatically convert one type to another when a method that's not recognised is called.
The pattern you're describing in particular is referred to as "enrich my library", after an article Martin Odersky wrote in 2006. It's still an okay introduction to what you want to do: http://www.artima.com/weblogs/viewpost.jsp?thread=179766
The way to do this is with an implicit conversion. These can be used to define views, and their use to enrich an existing library is called "pimp my library".
I'm not sure if you need to write a conversion from Try[Group] to Future[Set], or you can write one from Try to Future and another from Group to Set, and have them compose.