Step-by-step connection between high-order Scala function with examples given

I find it difficult to understand how to make the transition from the definition of a high-order function Scala to the given example. This was presented on this slide show on slide 81 .

Here is the definition of a higher order function:

trait X[A] { def map[B](f: A => B): X[B] } 

Here are some examples:

 (1 to 10) map { x => x * 2 } // evaluates to Vector(2, 4, ..., 20) (1 to 10) map { _ * 2 } // shorthand! 

Yes?! There simply are a few steps. I get that examples can use both function definitions and some Scala subtleties. I just lack the experience of reading Scala and the assumptions about the connection that have not yet been made.

My background is Java OO. Now I am learning Scala and functional programming. And this is not the first example that I did not understand. This is only the first where I felt that I had the courage to know that I would look ignorant.

I tried to research it. First, I went to Scala's β€œBible”, β€œProgramming in Scala 2nd Edition,” and tried to figure it out from there (pp. 165-9). Then I did a search on StackOverflow. And I found some links that talk about this area. But nothing really shows me, STEP-BY-STEP, the connection between the Scala high-order function definition and the examples given in a way that maps to a specific instance on this slide.

Here is what I found in StackOverflow:

I only now understand that I missed Google and immediately got into StackOverflow. Hmmm. If you google and find only the correct link, I would be pleased to see it. I did not have enough time to sift through all Google links that use terms such as monkey-monad, blastomorphisms, etc., Leaving me even more confused and less likely to try to figure it out.

+7
source share
7 answers

I think the following is just an example signature intended to display some properties of the Scala collection. In particular, it does not show any implementation, therefore you cannot connect all points. Also, this does not correspond to all examples ... So, maybe this is confusing.

 trait X[A] { def map[B](f: A => B): X[B] } 

I would read this as: set the collection class X over elements of type A :

  • it has a map function that is parameterized on type B
  • The map function takes a function f that maps one A to one B
  • map returns a collection of the same type X over elements of type B

He then goes on to an example illustrating the use of:

 (1 to 10) map { x => x * 2 } 

So, connecting the dots:

  • Collection X is a type (1 to 10), here a Range
  • f: A => B x => x * 2 , which is called as a function that takes Int and returns and Int .
  • Given the signature, you think you will return Range on top of Int , but actually it returns IndexedSeq .

A better example would be:

 List(1, 2, 3).map(i => i + "!") // a List[Int] // returns a List[String]: List("1!", "2!", "3!") 
+6
source

A higher order function (or method) is a function / method that either takes a function as its parameter, or gives a function as its result, or both.

In this case, this is a method called map , defined for things like lists, arrays, and many other types of containers. When 1 to 10 , which is a range of numbers from 1 to 10, represented by Range[Int] in Scala, is called, it goes around them one by one and applies a function (the one that was passed as a parameter) to each number within the range. The results of this function are accumulated in a new container - Vector[Int] in this case, which is returned as a result of the map method.

So (1 to 10) map { x => x * 2 } , which is the btw syntactic sugar for (1 to 10).map(x => x * 2) , applies x => x * 2 to numbers from 1 to 10 . You can think of it as a callback function. You could also write this as follows:

 (1 to 10).map( new Function1[Int, Int] { override def apply(x: Int) = x * 2 }) 
+6
source

Determine the data type using the map method, a separately linked list.

 sealed abstract class MyList[+A] { def map[B](f: A => B): MyList[B] // higher order function declaration. def head: A def tail: MyList[A] } case class Cons[A](head: A, tail: MyList[A]) extends MyList[A] { def map[B](f: A => B): MyList[B] = Cons[B](f(head), tail.map(f)) } case object Nil extends MyList[Nothing] { def map[B](f: Nothing => B): MyList[B] = this def head = sys.error("head on empty list") def tail = sys.error("tail on empty list") } 

The list is either empty or represents a single value ( head ) paired with the rest of the list ( tail ). We present two cases as a hierarchy of classes extending from the private parent class MyList .

Pay attention to the implementation of Cons#map , we used the f parameter twice, firstly, to execute the function with the head , and secondly, go to the tail.map recursive call.

The syntax f(head) is short for f.apply(head) , the value f is an instance of the Function1 class that the apply method defined.

So far so good. Create a list.

 val list: MyList[Int] = Cons(1, Cons(2, Nil)) 

Now we want to convert the Ints list to a new String s list, converting each element. In Java, you are explicitly an anonymous subclass of Function1 as follows.

 // longhand: val stringList1: MyList[String] = list.map[String](new Function1[Int, String] { def apply(a: Int): String = a.toString }) 

This is legal in Scala, but the signal to noise ratio is small. Use the syntax of an anonymous function instead.

 val stringList2: MyList[String] = list.map[String]((a: Int) => a.toString) 

We can go further and omit explicit type annotations, where the compiler has enough information to output them.

First, let's specify the parameter type a , based on the type of the list element.

 val stringList3: MyList[String] = list.map[String](a => a.toString) 

In fact, simple functions like these can also be expressed with placeholder syntax. Instead of declaring parameters, just write the code and use _ for any unknown value. By doing this, as well as allowing you to stringList4 type stringList4 :

 val stringList4 = list.map(_.toString) 
+5
source

Let's just focus on the map method, as defined in the definition of your attribute X

 def map[B](f: A => B): X[B] 

So this is a method with one parameter f . Type f (bit after the colon) A => B This is a type of function; this is a shorthand for the trait Function1[A, B] , but I prefer not to think about these traits at all and just as something that could lead to B for the given value A.

So: the function A => B is what takes one instance of type A and creates one instance of type B Here are some examples of declaring them

 val f = (i: Int) => i.toString //Int => String val g = (_ : String).length //String => Int, using placeholder syntax 

So now think about X[A] ; it may be a collection type such as List . If you have the List[String] function and String => Int g above, then you can obviously get the List[Int] in element by applying this function to each element in the list, building a new list using the results.

So now you can say:

 strings map g //strings is a List[String] 

But Scala allows you to declare a function anonymously. What does it mean? Well, that means you can declare it at the inline point of use, instead of declaring it as val or var. Often they are called "lambdas." The syntax you are puzzling are two options for such functions.

 strings map { (x: String) => x.length } strings map { x => x.length } strings map { _.length } strings map ( _.length ) 

All of this is basically the same thing. The first clearly declares the transferred function to the map. Since Scala has type inference, you can use function input type in this case. The placeholder _ syntax is used instead of the identifier x and is a nice bit of sugar if you need to access the input only once. And in many cases, you can use parens instead of braces, with the exception of functions with multiple operators.

+3
source

Your example refers to the Scala Framework Collection, which itself uses some complex use of the type system to get the most specific type when converting the collection. Now, the mechanism that makes this difficult to understand is not really relevant to this example. A higher-order function is simply a function or method that takes (or returns) other functions as an argument. The example is somewhat obscured by adding type parameters and not mentioning the use of Scala Framework Frameworks implicits.

+2
source

Not sure what exactly you are not getting, but explain the examples:

 trait X[A] { def map[B](f: A => B): X[B] } 

A trait is like a Java interface, which can also have specific methods.

X is the name of the attribute, and [A] is the type parameter - I think Java generics <A> . (Often A , B , etc. Used for item types in collections and T elsewhere, but this is just a convention.)

The characteristic indicates a member with the name map , which is a method with another parameter of type [B] and takes an argument of a function of type A => B , with a return type of X[B] . It is abstract here because there is no method body.

A bit that may be missing is that A => B not suitable for Function1[A, B] . Function1 - type of function objects that take 1 argument. (A, B) => C not suitable for Function2[A, B, C] , etc. You can create your own Function types in Java - this is a fun exercise. A functional object is essentially only one that has an apply method that creates a result from some arguments.

 (1 to 10) map { x => x * 2 } 

These include point notation, where a.method(b) written as a method b . So, to is a method on RichInt that takes a Int and creates a Range . map is a Range method that takes an argument to Function1 (remember that a function is just an object of type Function1 ).

=> also used to write the functions themselves (in addition to the level type, as described above). So, everything is the same: all objects of type Int => Int :

 (x: Int) => x + 1 new Function1[Int, Int] { def apply(x: Int) = x + 1 } // note Function1 is a trait, not a class, // so this the same as `new Object with Function[Int, Int]` new (Int => Int) { def apply(x: Int) = x + 1 } 

Scala uses type inference, so you do not need to add all types yourself if a specific function type (or any other parameterized type) is expected from the context, for example

 val f : Int => Int = x => x + 1 val f : Int => Int = _ + 1 

I hope you see what this superscript notation means. The underline is useful, because otherwise there will always be some kind of repetition, since the RHS definition of the function must use the parameters from the LHS. Another example would be a function matching a String with its length:

 val f: String => Int = _.length 

Since vals types are usually inferred, you can provide only the necessary type annotation

 val f = (_: String).length 

This is probably a bit confusing because syntactic sugar and type inference mean that there are several ways to write the same thing, but once you get it, you will find it there to make your life easier and reduce noise. Play REPL with them if you haven't already.

+2
source

Scala: (1 to 10) map { x => x * 2 }
English: take values ​​from 1 to 10 and multiply each by 2.

Some notes:

  • (1 to 10), Scala recognizes that it is a set of integers, in particular Range [Int]. It can be converted to another type of collection, for example. (1 to 10).toList

  • card, has a lower case. Think of a verb to display from a thing to another.

  • {x => x * 2}, surrounded by curly braces. This means that it is a function without a name, an anonymous function.

  • Submenu (_) can be replaced with x => x


Scala: trait X[A] { def map[B](f: A => B): X[B] }
English language:
We define a trait that we can add to class X, such as A.
It has a method that takes a value and maps it to a different value for the new class X.

Note:

  • X[A] and X[B] have the same collection type, but can have elements of different types, for example. `(1 to 10) .List map {_.toSTring} will display the list [Int] in the list [String].

  • f: A => B , this means that the card takes a function as an argument and has one parameter of type A and returns type B.

  • map defined in all types of the Scala collection. Usually you do not define it yourself.

+1
source

All Articles