Closure in Golang

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Preface

Golang Follow the design philosophy of "less is more" while supporting closures (Closure), then closures are certainly of great value to Golang.

For Golang beginners, there will be a few questions:

    1. What is a closure?
    2. How are closures generated?
    3. What problems can be solved by closures?

Closures are widely used in functional programming, so when you mention closures, readers are bound to think of functional programming, and we'll look at it briefly.

In the past more than 10 years, object-oriented programming has been so much so that in college education, teachers will only teach us two programming models: process-oriented and object-oriented. It is not known that functional programming has been around for decades before object-oriented thinking is produced.

The definition of functional programming in Wikipedia:

In computer science, functional programming are a programming paradigm that treats computation as the evaluation of Mathema Tical functions and avoids state and mutable data.
As a simple translation, functional programming is a programming model that considers computer operations to be calculations of functions in mathematics and avoids the concept of state and variable.

Closures are entities that are composed of functions and their associated reference environments, that is, closures = function + reference environment .
This definition is literally hard to understand, especially for readers who have been programming with imperative language, so this article will be elaborated in conjunction with code examples.

What is a function

function is a piece of executable code, after compiling "cured", each function in memory only one instance, get the function entry point can execute function. In a functional programming language, a function is a first-class citizen, and we do not need a function pointer, as in imperative language, to delegate an action function, and a function can be assigned to a variable as a parameter or return value of another function. Functions can be nested definitions (nested functions are generally anonymous functions), that is, within one function can define another function, with the nested function of this structure, will produce closures.

In object-oriented programming, we transmit the object, and in functional programming, we pass the function.
In functional programming, the higher-order function is a function that satisfies at least the following two points:

    1. function can be passed as a parameter
    2. function can be output as a return value

anonymous functions

An anonymous function is a function implementation that does not need to define a function name, and it is not a new concept that can be traced back to the 1958 Lisp language. However, for various reasons, C and C + + have not supported anonymous functions.

An anonymous function consists of a function declaration and a function body without a function name, such as:

func(x,y int) int {    return x + y}

In Golang, all functions are value types, that is, they can be passed as arguments, and can be passed as return values.

An anonymous function can assign a value to a variable:

f := func() int {    ...}

We can define a type of function:

type CalcFunc func(x, y int) int

Functions can be passed as values:

func AddFunc(x, y int) int {    return x + y}func SubFunc(x, y int) int {    return x - y}...func OperationFunc(x, y int, calcFunc CalcFunc) int {    return calcFunc(x, y)}func main() {    sum := OperationFunc(1, 2, AddFunc)    difference := OperationFunc(1, 2, SubFunc)    ...}

The function can be returned as a value:

// 第一种写法func add(x, y int) func() int {    f := func() int {        return x + y    }    return f}// 第二种写法func add(x, y int) func() int {    return func() int {        return x + y    }}

When a function returns multiple anonymous functions, it is recommended to use the first notation:

func calc(x, y int) (func(int), func()) {    f1 := func(z int) int {        return (x + y) * z / 2    }    f2 := func() int {        return 2 * (x + y)    }    return f1, f2}

There are two ways to call anonymous functions:

// 通过返回值调用func main() {    f1, f2 := calc(2, 3)    n1 := f1(10)    n2 := f1(20)    n3 := f2()    fmt.Println("n1, n2, n3:", n1, n2, n3)}// 在匿名函数定义的同时进行调用:花括号后跟参数列表表示函数调用func safeHandler() {    defer func() {        err := recover()        if err != nil {            fmt.Println("some exception has happend:", err)        }    }()    ...}

The nature of closures

Closures are blocks of code that contain free variables that are not defined within the code block or in any global context, but are defined in the context in which the code block is defined. Since free variables are included in the code block, as long as the closures are used, the free variables and the objects they refer to are not freed, and the code to be executed provides a binding computing environment for free variables.
The value of a closure is that it can be used as a function object or as an anonymous function, which means, for a type system, not only to represent the data but also to represent the code. Most languages that support closures use functions as first-level objects, meaning that these functions can be stored in variables as arguments to other functions, and most importantly, they can be dynamically created and returned by a function.

Closures in Golang also refer to variables outside the function, and the implementation of closures ensures that as long as closures are used, the variables referenced by the closures persist. Formally, the anonymous function is a closed packet.

Let's look at an example:

func add(n int) func(int) int {    sum := n    f := func(x int) int {        var i int = 2        sum += i * x        return sum    }    return f}func main() {    f1 := add(10)    n11 := f1(3)    n12 := f1(6)    f2 := add(20)    n21 := f2(4)    n22 := f2(8)}

In this example, the function variable is f, the free variable is sum, and F provides a binding computing environment for sum, so that sum and F are stuck together, and their block of code is the closure. The return value of the Add function is a closure, not just the address of the F function. In this closure function, only the internal anonymous function f can access the local variable I, and cannot be accessed by other means, so the closure guarantees the security of I .

When we inject the add function separately with different parameters (10, 20) and get different closure function variables, the result is isolated, that is, each time the Add function is called, a new local variable sum is generated and saved.
According to the rules of the imperative language, the Add function simply returns the address of the inline function f, but when the F function is executed, an error occurs because the sum variable cannot be found within its scope. In a functional language, when the inline function body refers to variables outside the body, the reference environment and function bodies involved in the definition are packaged into a whole (closure) return. There is no difference between the use of closures and normal function calls.

Now we give the definition of a reference environment: a collection of all active constraints at a point in the execution of a program, where the constraint refers to the relationship between the name of a variable and the object it represents.
So we say "closure = function + Reference environment"

Each time the Add function is called, a new closure instance is returned, which is isolated from each other and contains a different reference environment site at the time of invocation. Unlike functions, closures can have multiple instances at run time, and different reference environments and the same combination of functions can produce different instances.

In fact, we can think of the closure function as a class (C + +), a closure function call is to instantiate a class, the free variable of the closure is the member variable of the class, the parameter of the closure function is the parameter of the function object of the class. In this example, F1 and F2 can be considered as two objects instantiated, and Ni1 and Ni2 (i=1,2) can be considered as the return values of two invocations (different parameters) of the function object.
This reminds us of a famous saying: objects are data with behavior, and closures are data-attached behavior .

Description: A function object in C + + refers to the ability of an object to have functions, that is, the class needs to overload the operator "()"

Application of closures

Avoid abnormal crashes when the program is running

Golang provides an error interface for general error handling, and provides two built-in functions panic and recover for unforeseen error (exception) handling. The error interface is similar to the fault code in C + +, panic and recover similar to Try/catch/throw in C + +.
When the Panic () function is called during the execution of a function, the normal function execution process terminates immediately, but the statement in the function that was previously deferred by the DEFER keyword is executed normally, and then the function returns to the calling function and causes the panic process to be executed step-by-layer. Until all functions that are executing in the owning goroutine are terminated. The error message is reported, including the parameters passed in when the Panic () function is called, which is called the exception handling process.
The Recover function is used to terminate the error handling process. In general, recover should be executed in a function that uses the DEFER keyword to effectively intercept the error-handling process. If the recovery procedure is not explicitly called in the Goroutine where the exception occurred (calling the Recover function), it causes the process that the goroutine belongs to print the exception information and exits directly.

For third-party library calls, it is best to unify the recover process at the adaptation layer if it is unclear if there is a panic, otherwise it will cause the current process to exit abnormally, which is not what we expected.
The simple implementation is as follows:

func thirdPartyAdaptedHandler(...) {    defer func() {        err := recover()        if err != nil {            fmt.Println("some exception has happend:", err)        }    }()    ...}

This is a simple example, and we'll look at a more complicated example: using closures to make the business logic handlers of your website run more securely.

We define a function called Safehandler that wraps all the business logic handlers (Listhandler, Viewhandler, and Uploadhandler) once. The Safehandler function has a parameter and returns a value, both the passed in parameter and the return value are a function and both are HTTP. Handlerfunc type, this type of function has two parameters: http. Responsewriter and *http. Request. In fact, we're just going to pass the business logic handler as a parameter into the Safehandler () method so that we can intercept any error-handling process as it goes up, and thus prevent the program from running.

func safeHandler(fn http.HandlerFunc) http.HandlerFunc {    return func(w http.ResponseWriter, r *http.Request) {        defer func() {            if e, ok := recover().(error); ok {            http.Error(w, err.Error(), http.StatusInternalServerError)            fmt.Println("WARN: panic in %v - %v", fn, e)            fmt.Println(string(debug.Stack()))            }        }()        fn(w, r)    }}

Template method

I wrote an article in front of the "template method in Golang", through the interface and combination of methods to implement the template method, we use the closure of the way to simulate a template method similar to a simple example.

Define a conversion function type:

type Traveser func(ele interface{})

function of the process function: traveser processing of the slice array

func Process(array interface{}, traveser Traveser) error {    ...    traveser(array)    ...    return nil}

sortbyascending function: Sort data in the data slice in ascending order:

func SortByAscending(ele interface{}) {    ...}

sortbydescending function function: Sort data in descending order data slices:

func SortByDescending(ele interface{}) {    ...}

Process function Call:

func main() {    intSlice := make([]int, 0)    intSlice = append(intSlice, 3, 1, 4, 2)    Process(intSlice, SortByDescending)    fmt.Println(intSlice) //[4 3 2 1]    Process(intSlice, SortByAscending)    fmt.Println(intSlice) //[1 2 3 4]}

The template method pattern is the framework for defining an algorithm in an operation, and some steps are deferred to subclasses so that subclasses can redefine some specific steps of the algorithm without changing the framework of an algorithm. In Golang, the template method can be implemented not only by interface and combination, but also by the way of closure.

Security of variables

A local variable defined within a closure can only be accessed by an internal anonymous function and cannot be accessed by other means, which guarantees the security of the variable.

callback function

Closures are often used for callback functions. When IO operations (such as fetching data from the network, file read and write) are completed, certain operations are performed on the acquired data, which can be passed to the function object for processing.

Summary

The concept of

Closures is literally hard to understand, especially for readers who have been programming with imperative languages. Through the Golang code, this paper clarifies several puzzles of the beginners of closure, deeply analyzes the essence of closure, and finally shares the four applications of closure in Golang, hoping to help readers a certain extent.

Related Article

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.