Home  >  Article  >  Backend Development  >  A brief analysis of closures in Golang

A brief analysis of closures in Golang

青灯夜游
青灯夜游forward
2022-11-21 20:36:036504browse

A brief analysis of closures in Golang

1. What is closure?

Before we actually talk about closures, let’s lay some groundwork:

  • Functional programming
  • Function scope
  • Inheritance relationship of scope

[Related recommendations: Go video tutorial

1.1 Prerequisite knowledge

1.2.1 Functional programming

Functional programming is a programming paradigm, a way of looking at problems. Every function is organized in small functions Become a larger function, the parameters of the function are also functions, and the function returns are also functions. Our common programming paradigms are:

  • Imperative programming:
    • The main idea is: focus on the steps the computer executes, that is, tell the computer step by step what to do first and then what to do.
    • First standardize the steps to solve the problem and abstract it into a certain algorithm, and then write a specific algorithm to implement it. Generally, as long as the language supports the procedural programming paradigm, we can call it a procedural programming language, such as BASIC. C etc.
  • Declarative programming:
    • The main idea is to tell the computer what it should do, but does not specify how to do it, such as SQL, HTML for web programming, and CSS.
  • Functional programming:
    • Only focuses on what to do rather than how to do it. There is a trace of declarative programming, but it focuses more on "function is the first The principle of "bit" means that functions can appear anywhere, parameters, variables, return values, etc.

Functional programming can be considered the opposite of object-oriented programming. Generally, only some programming languages ​​will emphasize a specific programming method, and most languages ​​​​are multi-paradigm languages. , can support a variety of different programming methods, such as JavaScript, Go, etc.

Functional programming is a way of thinking. It regards computer operations as the calculation of functions. It is a methodology for writing codes. In fact, I should talk about functional programming, and then talk about closures. Because closure itself is one of the characteristics of functional programming.

In functional programming, a function is a first-class object, which means that a function can be used as an input parameter value for other functions, or it can be retrieved from a function The return value is modified or assigned to a variable. (Wikipedia)

Generally pure functional programming languages ​​do not allow direct use of program state and variable objects. Functional programming itself is to avoid the use of shared state,Variable state, avoid side effects as much as possible.

Functional programming generally has the following characteristics:

  • Functions are first-class citizens: functions are placed first and can be used as parameters, assigned values, and Passed, can be used as a return value.

  • No side effects: the function must remain purely independent, cannot modify the value of external variables, and does not modify external states.

  • Reference transparency: Function operation does not depend on external variables or states. For the same input parameters, the return value should be the same in any case.

1.2.2 Function scope

Scope (scope), programming concept, generally speaking, The name used in a piece of program code is not always valid/available, and the code scope that limits the availability of the name is the scope of the name.

In layman’s terms, function scope refers to the range in which a function can work. A function is a bit like a box, one layer inside another. We can understand the scope as a closed box, that is, the local variables of the function, which can only be used inside the box and become an independent scope.

A brief analysis of closures in Golang

#The local variable within the function jumps out of the scope after leaving the function, and the variable cannot be found. (The inner function can use the local variables of the outer function, because the scope of the outer function includes the inner function). For example, the following innerTmep cannot find the variable outside the function scope, but outerTemp can still be used in inner functions.

A brief analysis of closures in Golang

Regardless of any language, there is basically a certain memory recycling mechanism, that is, recycling unused memory space. The recycling mechanism is generally the same as the scope of the function mentioned above. It is related. When a local variable goes out of its scope, it may be recycled. If it is still referenced, it will not be recycled.

1.2.3 Inheritance relationship of scope

The so-called scope inheritance means that the small box mentioned earlier can inherit the scope of the outer large box, and can be taken out directly from the small box Things in the big box, but the big box cannot take out the things in the small box, unless escape occurs (escape can be understood as giving a reference to the things in the small box, and the big box can be used as soon as it is obtained). Generally speaking, there are two types of scopes of variables:

  • Global scope: applies anywhere

  • Local scope: general It is a code block, within a function or package, The variables declared/defined inside the function are called local variables, the scope is limited to the inside of the function

1.2 Definition of closure

"In most cases we do not understand first and then define, but define first and then understand", first Define it,

It doesn’t matter if you don’t understand it:

Closure is a reference to

a function and its bundled surrounding environment (lexical environment, lexical environment) The combination. In other words, closures allow developers to access the scope of an outer function from an inner function. Closures are created when the function is created.

One sentence expression:

##closure=Function Reference environmentClosure = function reference environment

The words Go language cannot be found in the above definition. Smart students must know that closure has nothing to do with language. It is not unique to JavaScript or Go, but Functional programming language is unique, yes, you read it right, Any language that supports functional programming supports closures, Go and JavaScript are two of them, and the current version of Java also supports closures, But some people may think that it is not a perfect closure, the details are discussed in the article.

1.3 How to write closure

1.3.1 First look at closure

The following is A piece of closure code:

import "fmt"

func main() {
	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())
}
func lazySum(arr []int) func() int {
	fmt.Println("先获取函数,不求结果")
	var sum = func() int {
		fmt.Println("求结果...")
		result := 0
		for _, v := range arr {
			result = result + v
		}
		return result
	}
	return sum
}

Output result:

先获取函数,不求结果
等待一会
求结果...
结果: 15

It can be seen that the sum() method inside can refer to the external function lazySum() parameters and local variables, when lazySum() returns function sum(), the relevant parameters and variables are saved in the returned function and can be processed later transfer.

The above function may go one step further to reflect the bundled function and its surrounding status. We add a number of times count:

import "fmt"
func main() {
	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())
	fmt.Println("结果:", sumFunc())
	fmt.Println("结果:", sumFunc())
}func lazySum(arr []int) func() int {
	fmt.Println("先获取函数,不求结果")
	count := 0
	var sum = func() int {
		count++
		fmt.Println("第", count, "次求结果...")
		result := 0
		for _, v := range arr {
			result = result + v
		}		return result
	}	return sum
}

What does the above code output? ? Will the number of times count change? count is obviously a local variable of the outer function, but in the memory function reference (bundling), the inner function is exposed. The execution results are as follows :

先获取函数,不求结果
等待一会
第 1 次求结果...
结果: 15
第 2 次求结果...
结果: 15
第 3 次求结果...
结果: 15

The result is count In fact, it will change every time. To summarize this situation:

  • There is another function nested in the function body, and the return value is a function.
  • The inner function is exposed and referenced by places other than the outer function, forming a closure.
Someone may have questions at this time. The previous one is

lazySum() was created once and executed 3 times, but if it is executed 3 times, it will be created differently. , what will it be like? Experiment:

import "fmt"
func main() {
	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())

	sumFunc1 := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc1())

	sumFunc2 := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc2())
}func lazySum(arr []int) func() int {
	fmt.Println("先获取函数,不求结果")
	count := 0
	var sum = func() int {
		count++
		fmt.Println("第", count, "次求结果...")
		result := 0
		for _, v := range arr {
			result = result + v
		}		return result
	}	return sum
}

The execution results are as follows, each execution is the first time:

先获取函数,不求结果
等待一会
第 1 次求结果...
结果: 15
先获取函数,不求结果
等待一会
第 1 次求结果...
结果: 15
先获取函数,不求结果
等待一会
第 1 次求结果...
结果: 15

It can be seen from the above execution results:

Close When the package is created, one copy of the referenced external variable count has already been created, that is, it does not matter if they are called individually. Continue to raise a question, **If a function returns two functions, is this one closure or two closures? ** Let’s practice it below:

Return two functions at a time, one for calculating the result of the sum, and one for calculating the product:

import "fmt"
func main() {
	sumFunc, productSFunc := lazyCalculate([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())
	fmt.Println("结果:", productSFunc())
}func lazyCalculate(arr []int) (func() int, func() int) {
	fmt.Println("先获取函数,不求结果")
	count := 0
	var sum = func() int {
		count++
		fmt.Println("第", count, "次求加和...")
		result := 0
		for _, v := range arr {
			result = result + v
		}		return result
	}	var product = func() int {
		count++
		fmt.Println("第", count, "次求乘积...")
		result := 0
		for _, v := range arr {
			result = result * v
		}		return result
	}	return sum, product
}

The running results are as follows:

先获取函数,不求结果
等待一会
第 1 次求加和...
结果: 15
第 2 次求乘积...
结果: 0

As can be seen from the above results, closure is when a function returns a function. No matter how many return values ​​(functions) there are, it is a closure. If the returned function uses external function variables, they will be bound together and affect each other. :

A brief analysis of closures in GolangThe closure binds the surrounding state. I understand that the function at this time has the state, so that the function has all the capabilities of the object, and the function has the state.

1.3.2 Pointers and values ​​in closures

In the above example, all values ​​used in our closures are numerical values. If we pass the pointer, it will be How is it?

import "fmt"
func main() {
	i := 0
	testFunc := test(&i)
	testFunc()
	fmt.Printf("outer i = %d\n", i)
}func test(i *int) func() {
	*i = *i + 1
	fmt.Printf("test inner i = %d\n", *i)	return func() {
		*i = *i + 1
		fmt.Printf("func inner i = %d\n", *i)
	}
}

The running results are as follows:

test inner i = 1
func inner i = 2
outer i = 2

It can be seen that if it is a pointer, if the value of the address corresponding to the pointer is modified in the closure, it will also affect the value outside the closure. This is actually very easy to understand. There is no reference passing in Go, only value passing. When we pass a pointer, it is also passed by value. The value here is the value of the pointer (which can be understood as the address value).

When the parameter of our function is a pointer, the parameter will copy a copy of the pointer address and pass it as a parameter. Because the essence is still an address, internal modifications can still have an impact on the outside.

The data in the closure actually has the same address. The following experiment can prove it:

func main() {
	i := 0
	testFunc := test(&i)
	testFunc()
	fmt.Printf("outer i address %v\n", &i)
}
func test(i *int) func() {
	*i = *i + 1
	fmt.Printf("test inner i address %v\n", i)
	return func() {
		*i = *i + 1
		fmt.Printf("func inner i address %v\n", i)
	}
}

The output is as follows, so it can be inferred that if the closure refers to the pointer data of the external environment, it will only Copy a copy of the pointer address data instead of copying the real data (==Leave a question first: when is the timing of the copy==):

test inner i address 0xc0003fab98
func inner i address 0xc0003fab98
outer i address 0xc0003fab98

1.3.2 Closure delay

The above examples seem to tell us that when the closure is created, the data has been copied, but is this really the case?

The following is a continuation of the previous experiment:

func main() {
	i := 0
	testFunc := test(&i)
	i = i + 100
	fmt.Printf("outer i before testFunc  %d\n", i)
	testFunc()
	fmt.Printf("outer i after testFunc %d\n", i)
}func test(i *int) func() {
	*i = *i + 1
	fmt.Printf("test inner i = %d\n", *i)
		return func() {
		*i = *i + 1
		fmt.Printf("func inner i = %d\n", *i)
	}
}

After we create the closure, we change the data and then execute the closure. The answer must be that it really affects the execution of the closure, because they all They are pointers, all pointing to the same data:

test inner i = 1
outer i before testFunc  101
func inner i = 102
outer i after testFunc 102

Suppose we change the way of writing and let the variables in the external environment of the closure be modified after declaring the closure function:

import "fmt"

func main() {
	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())
}
func lazySum(arr []int) func() int {
	fmt.Println("先获取函数,不求结果")
	count := 0
	var sum = func() int {
		fmt.Println("第", count, "次求结果...")
		result := 0
		for _, v := range arr {
			result = result + v
		}
		return result
	}
	count = count + 100
	return sum
}

Actual execution As a result,

count

will be the modified value: <pre class="brush:js;toolbar:false;">等待一会 第 100 次求结果... 结果: 15</pre>This also proves that the closure does not actually declare

var sum = func() int {.. .}

After this sentence, the count of the external environment is bound to the closure. It is only bound when the function returns the closure function. This is Lazy binding.

如果还没看明白没关系,我们再来一个例子:

func main() {
	funcs := testFunc(100)
	for _, v := range funcs {
		v()
	}
}
func testFunc(x int) []func() {
	var funcs []func()
	values := []int{1, 2, 3}
	for _, val := range values {
		funcs = append(funcs, func() {
			fmt.Printf("testFunc val = %d\n", x+val)
		})
	}
	return funcs
}

上面的例子,我们闭包返回的是函数数组,本意我们想入每一个 val 都不一样,但是实际上 val都是一个值,==也就是执行到return funcs 的时候(或者真正执行闭包函数的时候)才绑定的 val值==(关于这一点,后面还有个Demo可以证明),此时 val的值是最后一个 3,最终输出结果都是 103:

testFunc val = 103
testFunc val = 103
testFunc val = 103

以上两个例子,都是闭包延迟绑定的问题导致,这也可以说是 feature,到这里可能不少同学还是对闭包绑定外部变量的时机有疑惑,到底是返回闭包函数的时候绑定的呢?还是真正执行闭包函数的时候才绑定的呢?

下面的例子可以有效的解答:

import (
	"fmt"
	"time"
)

func main() {
	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
	fmt.Println("等待一会")
	fmt.Println("结果:", sumFunc())
	time.Sleep(time.Duration(3) * time.Second)
	fmt.Println("结果:", sumFunc())
}
func lazySum(arr []int) func() int {
	fmt.Println("先获取函数,不求结果")
	count := 0
	var sum = func() int {
		count++
		fmt.Println("第", count, "次求结果...")
		result := 0
		for _, v := range arr {
			result = result + v
		}
		return result
	}
	go func() {
		time.Sleep(time.Duration(1) * time.Second)
		count = count + 100
		fmt.Println("go func 修改后的变量 count:", count)
	}()
	return sum
}

输出结果如下:

先获取函数,不求结果
等待一会
第 1 次求结果...
结果: 15
go func 修改后的变量 count: 101
第 102 次求结果...
结果: 15

第二次执行闭包函数的时候,明显 count被里面的 go func()修改了,也就是调用的时候,才真正的获取最新的外部环境,但是在声明的时候,就会把环境预留保存下来。

其实本质上,Go Routine的匿名函数的延迟绑定就是闭包的延迟绑定,上面的例子中,go func(){}获取到的就是最新的值,而不是原始值0

总结一下上面的验证点:

  • 闭包每次返回都是一个新的实例,每个实例都有一份自己的环境。
  • 同一个实例多次执行,会使用相同的环境。
  • 闭包如果逃逸的是指针,会相互影响,因为绑定的是指针,相同指针的内容修改会相互影响。
  • 闭包并不是在声明时绑定的值,声明后只是预留了外部环境(逃逸分析),真正执行闭包函数时,会获取最新的外部环境的值(也称为延迟绑定)。
  • Go Routine的匿名函数的延迟绑定本质上就是闭包的延迟绑定。

2、闭包的好处与坏处?

2.1 好处

纯函数没有状态,而闭包则是让函数轻松拥有了状态。但是凡事都有两面性,一旦拥有状态,多次调用,可能会出现不一样的结果,就像是前面测试的 case 中一样。那么问题来了:

Q:如果不支持闭包的话,我们想要函数拥有状态,需要怎么做呢?

A: 需要使用全局变量,让所有函数共享同一份变量。

但是我们都知道全局变量有以下的一些特点(在不同的场景,优点会变成缺点):

  • 常驻于内存之中,只要程序不停会一直在内存中。
  • 污染全局,大家都可以访问,共享的同时不知道谁会改这个变量。

闭包可以一定程度优化这个问题:

  • 不需要使用全局变量,外部函数局部变量在闭包的时候会创建一份,生命周期与函数生命周期一致,闭包函数不再被引用的时候,就可以回收了。
  • 闭包暴露的局部变量,外界无法直接访问,只能通过函数操作,可以避免滥用。

除了以上的好处,像在 JavaScript 中,没有原生支持私有方法,可以靠闭包来模拟私有方法,因为闭包都有自己的词法环境。

2.2 坏处

函数拥有状态,如果处理不当,会导致闭包中的变量被误改,但这是编码者应该考虑的问题,是预期中的场景。

闭包中如果随意创建,引用被持有,则无法销毁,同时闭包内的局部变量也无法销毁,过度使用闭包会占有更多的内存,导致性能下降。一般而言,能共享一份闭包(共享闭包局部变量数据),不需要多次创建闭包函数,是比较优雅的方式。

3、闭包怎么实现的?

从上面的实验中,我们可以知道,闭包实际上就是外部环境的逃逸,跟随着闭包函数一起暴露出去。

我们用以下的程序进行分析:

import "fmt"

func testFunc(i int) func() int {
	i = i * 2
	testFunc := func() int {
		i++
		return i
	}
	i = i * 2
	return testFunc
}
func main() {
	test := testFunc(1)
	fmt.Println(test())
}

执行结果如下:

5

先看看逃逸分析,用下面的命令行可以查看:

 go build --gcflags=-m main.go

A brief analysis of closures in Golang

可以看到 变量 i被移到堆中,也就是本来是局部变量,但是发生逃逸之后,从栈里面放到堆里面,同样的 test()函数由于是闭包函数,也逃逸到堆上。

下面我们用命令行来看看汇编代码:

go tool compile -N -l -S main.go

生成代码比较长,我截取一部分:

"".testFunc STEXT size=218 args=0x8 locals=0x38 funcid=0x0 align=0x0
        0x0000 00000 (main.go:5)        TEXT    "".testFunc(SB), ABIInternal, $56-8
        0x0000 00000 (main.go:5)        CMPQ    SP, 16(R14)
        0x0004 00004 (main.go:5)        PCDATA  $0, $-2
        0x0004 00004 (main.go:5)        JLS     198
        0x000a 00010 (main.go:5)        PCDATA  $0, $-1
        0x000a 00010 (main.go:5)        SUBQ    $56, SP
        0x000e 00014 (main.go:5)        MOVQ    BP, 48(SP)
        0x0013 00019 (main.go:5)        LEAQ    48(SP), BP
        0x0018 00024 (main.go:5)        FUNCDATA        $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
        0x0018 00024 (main.go:5)        FUNCDATA        $1, gclocals·d571c0f6cf0af59df28f76498f639cf2(SB)
        0x0018 00024 (main.go:5)        FUNCDATA        $5, "".testFunc.arginfo1(SB)
        0x0018 00024 (main.go:5)        MOVQ    AX, "".i+64(SP)
        0x001d 00029 (main.go:5)        MOVQ    $0, "".~r0+16(SP)
        0x0026 00038 (main.go:5)        LEAQ    type.int(SB), AX
        0x002d 00045 (main.go:5)        PCDATA  $1, $0
        0x002d 00045 (main.go:5)        CALL    runtime.newobject(SB)
        0x0032 00050 (main.go:5)        MOVQ    AX, "".&i+40(SP)
        0x0037 00055 (main.go:5)        MOVQ    "".i+64(SP), CX
        0x003c 00060 (main.go:5)        MOVQ    CX, (AX)
        0x003f 00063 (main.go:6)        MOVQ    "".&i+40(SP), CX
        0x0044 00068 (main.go:6)        MOVQ    "".&i+40(SP), DX
        0x0049 00073 (main.go:6)        MOVQ    (DX), DX
        0x004c 00076 (main.go:6)        SHLQ    $1, DX
        0x004f 00079 (main.go:6)        MOVQ    DX, (CX)
        0x0052 00082 (main.go:7)        LEAQ    type.noalg.struct { F uintptr; "".i *int }(SB), AX
        0x0059 00089 (main.go:7)        PCDATA  $1, $1
        0x0059 00089 (main.go:7)        CALL    runtime.newobject(SB)
        0x005e 00094 (main.go:7)        MOVQ    AX, ""..autotmp_3+32(SP)
        0x0063 00099 (main.go:7)        LEAQ    "".testFunc.func1(SB), CX
        0x006a 00106 (main.go:7)        MOVQ    CX, (AX)
        0x006d 00109 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX
        0x0072 00114 (main.go:7)        TESTB   AL, (CX)
        0x0074 00116 (main.go:7)        MOVQ    "".&i+40(SP), DX
        0x0079 00121 (main.go:7)        LEAQ    8(CX), DI
        0x007d 00125 (main.go:7)        PCDATA  $0, $-2
        0x007d 00125 (main.go:7)        CMPL    runtime.writeBarrier(SB), $0
        0x0084 00132 (main.go:7)        JEQ     136
        0x0086 00134 (main.go:7)        JMP     142
        0x0088 00136 (main.go:7)        MOVQ    DX, 8(CX)
        0x008c 00140 (main.go:7)        JMP     149
        0x008e 00142 (main.go:7)        CALL    runtime.gcWriteBarrierDX(SB)
        0x0093 00147 (main.go:7)        JMP     149
        0x0095 00149 (main.go:7)        PCDATA  $0, $-1
        0x0095 00149 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX
        0x009a 00154 (main.go:7)        MOVQ    CX, "".testFunc+24(SP)
        0x009f 00159 (main.go:11)       MOVQ    "".&i+40(SP), CX
        0x00a4 00164 (main.go:11)       MOVQ    "".&i+40(SP), DX
        0x00a9 00169 (main.go:11)       MOVQ    (DX), DX
        0x00ac 00172 (main.go:11)       SHLQ    $1, DX
        0x00af 00175 (main.go:11)       MOVQ    DX, (CX)
        0x00b2 00178 (main.go:12)       MOVQ    "".testFunc+24(SP), AX
        0x00b7 00183 (main.go:12)       MOVQ    AX, "".~r0+16(SP)
        0x00bc 00188 (main.go:12)       MOVQ    48(SP), BP
        0x00c1 00193 (main.go:12)       ADDQ    $56, SP
        0x00c5 00197 (main.go:12)       RET
        0x00c6 00198 (main.go:12)       NOP
        0x00c6 00198 (main.go:5)        PCDATA  $1, $-1
        0x00c6 00198 (main.go:5)        PCDATA  $0, $-2
        0x00c6 00198 (main.go:5)        MOVQ    AX, 8(SP)
        0x00cb 00203 (main.go:5)        CALL    runtime.morestack_noctxt(SB)
        0x00d0 00208 (main.go:5)        MOVQ    8(SP), AX
        0x00d5 00213 (main.go:5)        PCDATA  $0, $-1
        0x00d5 00213 (main.go:5)        JMP     0

可以看到闭包函数实际上底层也是用结构体new创建出来的:

A brief analysis of closures in Golang

uses the i on the heap:

A brief analysis of closures in Golang

That is, when returning the function, in fact Returns a structure, which records the reference environment of the function.

4. Let’s talk briefly

4.1 Does Java support closures?

There are many opinions on the Internet. In fact, although Java does not currently support return functions as return parameters, Java essentially implements the concept of closure, and the method used is The form of an inner class, because it is an inner class, is equivalent to having a reference environment, which is considered an incomplete closure.

Currently there are certain restrictions, such as final declaration, or a clearly defined value, which can be passed:

There are related answers on Stack Overflow: stackoverflow.com/questions/5…

A brief analysis of closures in Golang

4.2 What is the future of functional programming?

The following is the content of the Wiki:

Functional programming has long been popular in academia but has few industrial applications. The main reason for this situation is that functional programming is often considered to seriously consume CPU and memory resources [18]. This is because efficiency issues were not considered in the early implementation of functional programming languages, and they were function-oriented. Formula programming features, such as ensuring reference transparency, etc., require unique data structures and algorithms. [19]

However, recently several functional programming languages ​​have been used in commercial or industrial systems [20], for example:

  • Erlang, which was developed by the Swedish company Ericsson in the late 1980s, was originally used to implement fault-tolerant telecommunications systems. It has since been used as a popular language to create a series of applications in companies such as Nortel, Facebook, Électricité de France and WhatsApp. [21][22]Scheme, which was used for early Apple MacintoshBasic for several applications on computers, and has recently been applied in directions such as training simulation software and telescope control.
  • OCaml, which was launched in the mid-1990s, has found commercial applications in areas such as financial analysis, driver verification, industrial robot programming, and static analysis of embedded software. Haskell, although it was originally used as a research language, it has also been used by a series of companies in aerospace systems, hardware design, network programming and other fields.

Other functional programming languages ​​used in industry include multi-paradigm Scala[23], F#, and There are Wolfram Language, Common Lisp, Standard ML and Clojure, etc.

From my personal point of view, I am not optimistic about pure functional programming, but I believe that almost every advanced programming need will have the idea of ​​​​functional programming in the future. I especially look forward to Java embracing functional programming. Judging from the languages ​​I know, the functional programming features in Go and JavaScript are deeply loved by developers (of course, if you write bugs, you will hate them).

The reason why it has suddenly become popular recently is also because the world continues to develop and memory is getting larger and larger. The restrictions of this factor are almost liberated.

I believe that the world is colorful. It is impossible for one thing to rule the world. It is more about a hundred schools of thought contending. The same is true for programming languages ​​or programming paradigms. There may be masters in the future, and eventually history will Screen out those that are ultimately in line with the development of human society.

For more programming related knowledge, please visit: Programming Video! !

The above is the detailed content of A brief analysis of closures in Golang. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete