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

a 夏天 提交于 2019-12-21 19:49:08

问题


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.


回答1:


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



回答2:


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.



来源:https://stackoverflow.com/questions/31621321/scala-how-can-i-create-a-function-that-allows-me-to-use-dot-notation-when-calli

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!