Scala High-order functions (High-order function) anatomy

Source: Internet
Author: User

Scala is a functional programming language, meaning that each function is a value. Scala has a very concise syntax for defining anonymous functions, curry functions (curried function), application functions, partial functions, and nested functions. Functional programming is derived from mathematical functions and includes a series of concepts such as concatenation and parallel, combination and separation, covariance and contravariance. This article will explain how to achieve higher-order functions, and other parts of the content do not dig.

Type parametric covariance and inverse covariance

Type parameterization is made up of type covariance (types variance) supplements, and the mechanism for type covariance is by specifying such as covariance (convariant) and inverse covariance (contravariant) in the type system. Be constrained. The relationship of subtypes brings problems to covariance--what is the relationship between subtypes in type parameterization? In Scala, you can parameterize by class and trait. When using type parameters in class and trait, you can use the + sign for covariance (such as maybe). Type covariance (convriance) allows overloading and using narrow types in the covariant position of its parent class, such as the return value. That

Scala allows the covariance of type parameters to be defined by "+/-",

    1. The "+" is placed before the type parameter to indicate that the constructor is covariant for the parameter;
    2. "-" means inverse covariance;
    3. no symbol indicates non-covariant.
Covariance is equivalent to generic T in Java, and vice versa, and inverse covariance is not a type cast (cast). Because, this also involves the concept of variable (variant) and immutable (invariant).Mutable objects should remain unchanged

The variable (mutable) object should remain the same (invariant), why? In Scala, to achieve scalable, it is necessary to achieve the corresponding conversion capabilities. A type parameter should be invariant (invariant), whether it is covariant or inverse covariant. All Scala mutable collection classes are immutable. Let's use an example to illustrate why mutable objects should be immutable. We use contradiction to argue that now you can use Collection.immutable.List and the corresponding variable collection.mutable.ListBuffer. Since the Listbuffer is mutable, it is declared to be immutable:

Final class Listbuffer[a] ... { ... }

Note that if you declare an immutable type (invarint type), you need to remove the-and + marker symbols, because you can no longer specify other types for Listbuffer. Therefore, the following compilation error occurs:

Scala> val mxs:listbuffer[string] = Listbuffer ("Pants") mxs:scala.collection.mutable.listbuffer[string] = Listbuffer (pants) scala> val Everything:listbuffer[any] = Mxs<console>:6:error:type Mismatch;found: Scala.collection.mutable.listbuffer[string]required:scala.collection.mutable.listbuffer[any]val everything: Listbuffer[any] = Mxs

Although string is a subtype of Scala.any, Scala does not point mxs to everything, and in order to understand why, we assume that Listbuffer is mutable and that the following code does not have any compilation errors:

Scala> val mxs:listbuffer[string] = Listbuffer ("Pants") mxs:scala.collection.mutable.listbuffer[string] = Listbuffer (pants) scala> val Everything:listbuffer[any] = mxsscala> everything + = 1res4:everything.type = ListBuff ER (1, pants)

have you found the problem? Because everything is of any type, you cannot store any integer value into a set of character types, which is simply waiting for a disaster to occur. To avoid this type of problem, it is best to keep a Mutable object (Mutable objects) unchanged (invariant). What happens if the immutable object is in the collection? There is no problem with the covariance that is placed on immutable objects. If you change Listbuffer to list, you will get a list[string] instance that points to list[any] without any problems.

Scala> val xs:list[string] = list ("Pants") xs:list[string] = List (pants) scala> val Everything:list[any] = Xseveryt Hing:list[any] = List (pants)

the reason for this point is that the list is immutable, you can add 1 to the XS list, and he will return a new any type of listing:

Scala> 1:: Xsres5:list[any] = List (1, pants)

Again , the above method is correct, because the con (::) method always returns a new list, and its type depends on the type of the list element. The only type that can store both an shaping value type and a reference value type is Scala. Any. Keep in mind that this is an important property of mutable/immutable object covariance.

In fact, the function type and function value are just the syntax icing of the corresponding class and its instance. The function type S = = T is equivalent to the parameterized type Scala. Function1[s, T], this type is defined in the Scala standard class library:

Trait Function1[-p, +r] {...}

a function with more than one parameter can also be defined similarly, in general, the N-tuple function type: (T1, T2, ..., tn) = = (t) is interpreted as functionn[t1, T2, ..., TN, T]. In other words, a function is an object that has an apply method. For example, the anonymous function "+1": X:int = X+1, is an example of the following function Function1:

New Function1[int, int] {  def apply (x:int): int = x + 1}

Scala uses a minus sign (-) to indicate inverse covariance, plus (+) for covariance. In Function1, p is inverse covariant and r is covariant. In Scala, a function contains values and types. For example, Function1 represents any function that receives a parameter, and the question is why Function1 is covariant on the return type for the inverse covariance of the parameters.

Before we answer the question, we use the counter-argument method to demonstrate--what happens to parameter covariance and inverse covariance of return types? If there is such a covariant parameter:
Val Addone:function1[any, Int] = {X:int = x + 1}

because int is a subclass of Scala.any, covariant parameters should allow the above code to compile. The vulnerability of this code is that you can use any type parameter to invoke AddOne, as long as the parameter is a subclass of any. Doing so causes a series of problems because the function receives only int. Scala, as a type-safe (type-safe) language, does not allow you to do so. Another only possibility is that you need to declare the parameter type as immutable (invariant), but this makes the Function1 difficult to extend. The only possible solution to creating a type-safe function is the inverse covariant parameter type. You cannot use an inverse covariant return type, consider the following code:

Val Asstring:int = = Int = {X:int = = (X.tostring:any)}

This code is invalid because any is a superclass of int, and inverse covariance allows you to reach a generalized type from a narrow type. The following is the correct:

Asstring (10) + 20

the code finally adds 20 into a string value, which is obviously problematic. Scala's strongly typed system aborts the occurrence of such errors when dealing with parameter types and return types. The only possible way to implement a flexible, type-safe Function1 feature is the covariance of inverse covariance and return types of parameter types .

In addition to the parameter types and return values, consider the boundary problem of type subclass (subtyping), i.e. B <: A lower bound and B;: Constraints on the upper bound. For the time being, it is not to be discussed.

After considering the above questions, here's how the higher-order function is implemented.

Higher order functions

Functions are called higher-order functions as arguments or as functions that return values. There are a lot of high-order functions in Scala's immutable.list, so let's take a look at one of the map methods

Class List[+a] ... {  def map[b] (f:a = B): List[b]}

In programming languages, in addition to value passing (call-by-value), reference passing (call-by-value), name passing (call-by-name) and demand passing (Call-by-need) are included. In the code above, the function of f:a = B as a parameter is passed as a name. Because of different usage scenarios, the name pass can be a lambda anonymous function and a lexical closure. Advanced functions like map can be implemented by for-comrehension or recursively.

def Map[a,b] (xs:list[a],f:a=>b): list[b] = {  xs match{case    List () = Nil case    head::tail=>f (head) :: Map (tail,f)  }}

In the map code above, a high-order function that implements a type parameter pass is implemented by pattern matching and:: Combining. Where F indicates the function parameterized processing operation. Of course, you can also use the tail recursion to implement and do the currying conversion.

Add a test example:

@Test def testhighorderfunction () {  val xs = List (1, 2, 3)  //Anonymous functions are passed as parameters  Logger.info (S "${xs map (x:int) =&G T x + 1} ")  //anonymous function with only one parameter, omit parameter peripheral  logger.info (S" ${xs map (x = x + 1)} ")  //Pointfree-style notation, placeholder instead  Logger.info (S "${xs map (_ + 1)}")  //Transfer function name only, function already implemented packaging  def addone (x:int) = x + 1  logger.info (S "${xs map AddOne } ")  /**   * Recursive implementation of higher order functions   * @param xs List   * @param f function   *  /def Map[a, B] (xs:list[a], f:a = B): list[b] = {    xs match {case      List () = Nil case      head:: tail = f (head):: Map (tail, f)    }
   }  Logger.info (S "${map (XS, AddOne)}")}


Scala High-order functions (High-order function) anatomy

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.