Nicholas Montaño
Nicholas Montaño

Reputation: 955

Scala: How can I create a function that allows me to use dot notation when calling it?

I have been confused about this for a while, even despite reading the Scala Style Guide - Method Invocation several times.

I want to be able to call this method

def foldRVL[A,B](l: List[A], z: B)(f: (A, B) => B) = //"Right-Via-Left"
  l.reverse.foldLeft(z)((a, b) => f(b, a))

using dot notation like this List(1,2,3).foldRVL(0)(_ + _).

And not like this: foldRVL(List(1,2,3), 0)(_ + _).

Also, sometimes I've seen code that shows methods that actually either takes zero parameters in their signature, or one fewer parameters than I would expect them to take, and still properly take a parameter using dot-notation. How does this work? I ask this because those methods work with dot-notation, so maybe if I wrote something like that I could solve my problem.

Upvotes: 3

Views: 1636

Answers (2)

Nicholas Montaño
Nicholas Montaño

Reputation: 955

soong pointed out that I'm actually seeking the 'Pimp my library' pattern, which I then looked up, and implemented like this to handle the method in question:

implicit class BlingList[+A](l: List[A]) {
  def foldRVL[B](z: B)(f: (A, B) => B): B = //"Right-Via-Left"
    l.foldLeft(z)((a, b) => f(b, a))
}

As far as I understand, the key element to allowing dot-notation is having a class construct that takes parameters of the type that I want to have 'pimped'. In this case, I have a list, and I want to call foldRVL on the list after I write the list down, like this: List(something).foldRVL(z)(f: A => B). Therefore, I need a class that takes a List[A] parameter for me to be able to write a method like that in the first code snippet.

The implicit keyword is used so that I can add methods to the existing class List without having to create a separate Library of methods. Anytime a List is found prefixed before foldRVL, it will be implicitly converted into a BlingList because the compiler will see a List attached to a method that doesn't exist in class List. It will therefore look for any implicit methods defined in scope that have a foldRVL method and take a List as an argument, and it finds that the implicit class BlingList has the method foldRVL defined and takes a List[A]. Therefore, I can now write:

List(1,2,3).foldRVL(0)(_ + _) // in some IDE's, foldRVL will be underlined to show that
res0: Int = 6                 // an implicit conversion is being made

"A Scala 2.10 implicit class example" goes into more depth about this. My favorite pointer from that post is to put all the implicit classes that you expect to be using in your current package and any subpackages inside of a package object, that way you don't have to clutter any of your classes or objects with implicit class definitions, nor do you have to import them. The fact that they are using the same package will automatically import them thanks to the package object.

Upvotes: 1

Aivean
Aivean

Reputation: 10882

For the first part of your question, you probably need to look at implicit classes:

  implicit class RichRVLList[A](l:List[A]) {
    def foldRVL[B](z: B)(f: (A, B) => B) = //"Right-Via-Left"
      l.reverse.foldLeft(z)((a, b) => f(b, a))
  }

  List(1,2,3).foldRVL(1)(_ + _)  // output: res0: Int = 7

You can "enrich" existent class using implicit wrapper to "add" new methods.

As for the second part, probably you want implicit parameters. Implicit parameters are deduced from the current scope by type. There are some predefined implicit values, such as Numerics, that were used in the example below:

  def product[T](els:TraversableOnce[T])(implicit num:Numeric[T]) = {
    els.fold(num.one)((x1, x2) => num.times(x1, x2))
  }      

  product(List(1, 2, 3)) // res1: Int = 6
  product(List(1, 2.5, 3)) //res2: Double = 7.5

Upvotes: 5

Related Questions