Class arguments?

scala> implicit class Somebuddy(s:String){
     |     def test(f:String=>String) = f(s)
     | }
class Somebuddy

why this class definition can accept an argument (s:String)?
my experience is class definition that doesn’t accept arguments.

Thank you

I come from other language, for instance, in ruby I just yield self instance to the anonymous func, and the String class accepts nothing argument.

irb(main):001:0> class String
irb(main):002:1>   def mytest
irb(main):003:2>     if block_given?
irb(main):004:3>       yield self
irb(main):005:3>     end
irb(main):006:2>   end
irb(main):007:1> end
=> :mytest
irb(main):008:0> "hello".mytest {|s| s.reverse}
=> "olleh"

Thanks

Well, the answer is… Scala is different :smiley: But basically it’s a Scala feature to reduce the boilerplate code you normally would write when defining a class in Java.

This is explained in Chapter 1 of “Programming in Scala”:

First, Scala’s syntax avoids some of the boilerplate that burdens Java
programs. For instance, semicolons are optional in Scala and are usually left
out. There are also several other areas where Scala’s syntax is less noisy.
As an example, compare how you write classes and constructors in Java and
Scala. In Java, a class with a constructor often looks like this:

class MyClass { // this is Java
    private int index;
    private String name;
    public MyClass(int index, String name) {
        this.index = index;
        this.name = name;
    }
}

In Scala, you would likely write this instead:

class MyClass(index: Int, name: String)

Once again, I urge you to do a bit more serious study of Scala to properly learn the basics, by either taking one of the free online classes or using a book.

4 Likes

In Ruby, classes can be changed at runtime and methods can be added to existing classes, as you’re doing with the String class in your example code. But as Scala is a compiled language, you can’t change an existing compiled class, instead you can make external methods look like they belong to a class by using a wrapper class.

The parameter list after the class name is roughly equivalent to initialize in Ruby, but the parameters are accessible inside the whole object without needing to assign them to instance variables. Your Somebuddy class is a normal class, that takes a String in that constructor. In your method test you use that String that was passed in. You could use this like any other class:

new Somebuddy("hello").test(s => s.reverse)  // returns "olleh"

The part that makes this test method available directly on strings is implicit. Making a class implicit puts it in a special scope, that is handled at compile time. If the compiler finds some code someString.test(s => s.reverse), it will note that there is no method test in the String class. Before reporting an error, it will look at the implicit scope, to see if there is a class, that takes a single constructor parameter of the type we called the method on (here String) and has a method test which matches the signature. If there is one, the compiler adds the creation of the wrapper class, so that the code that actually is executed is new Somebuddy(someString).test(s => s.reverse) (the extra object creation can often be prevented by extending AnyVal, but I’ll not go into that).

With Scala 3, this pattern is no longer necessary, there is nicer syntax for it with the introduction of extension methods. But these are also resolved at compile time, and you also have to name the object you’re adding methods to

2 Likes

A absolute great answer, thank you @crater2150