banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

Implementing function chaining based on pipeline technology

title: Implementing Function Chaining with Pipeline Technology
date: 2022-07-04 10:40:00
toc: false
index_img: http://api.btstu.cn/sjbz/?lx=m_dongman&cid=10
category:

  • Go
    tags:
  • Sequential
  • Chaining
  • Support
  • Request
  • Method
  • Function

The term "Pipeline" originates from Unix Shell command line, where we can use the pipeline operator | to combine simple commands and achieve powerful functionality. For example, if we want to filter out the nginx process from the system process list, we can do it like this:

ps -ef | grep nginx 

Here, we use the pipeline to connect the basic Unix commands ps and grep, and filter out the nginx process from the result of ps -ef.

In functional programming, we can also use the concept of pipelines to chain simple functions and build more powerful functionality. The most common example is the use of function chaining, which is similar to the flow interface pattern in object-oriented programming and allows for chained processing.

This way, each function can focus on its own task and do it to the best of its ability. Then, by combining them using pipelines, we can build more complex business functionality. This also aligns with the SOLID design principle of single responsibility.

In our main function, we use pipelines to combine the Map-Reduce-Filter functional modules and achieve a chained function call:

package main

import (
  "fmt"
  "log"
)

type user struct {
  name string
  age  int
}

func main() {
  var users = []user{
    {
      name: "xmp",
      age:  11,
    },
    {
      name: "wlm",
      age:  24,
    },
    {
      name: "wjf",
      age:  30,
    },
    {
      name: "lm",
      age:  50,
    },
  }
  // The processor functions are called in the order they are declared
  result := sumAge(users, filterAge, mapAgeToSlice)

  fmt.Println("Total age (excluding ages greater than 40 and less than 20):", result)
}

func filterAge(users []user) interface{} {
  var result []user
  for _, user := range users {
    if user.age < 40 && user.age > 20 {
      result = append(result, user)
    }
  }
  return result
}

func mapAgeToSlice(users []user) interface{} {
  var result []int
  for _, user := range users {
    result = append(result, user.age)
  }
  return result
}

func sumAge(users []user, pips ...func([]user) interface{}) (result int) {
  var reusltSlice []int
  for _, f := range pips {
    runResult := f(users)
    switch runResult.(type) {
    case []user:
      users = runResult.([]user)
    case []int:
      reusltSlice = runResult.([]int)
    }
  }
  if len(reusltSlice) == 0 {
    log.Fatalln("The mapAgeToSlice method is not added to the pipeline")
  }

  for _, age := range reusltSlice {
    result += age
  }
  return result
}

Here, we introduce a user struct to replace the dictionary type, making the code more concise and readable. The details about struct types will be explained in the next chapter on Go type system.

Then, we remove the closure functions in the Filter and Map functions and implement them directly in the code to simplify it. To facilitate the unified declaration of Filter and Map functions through pipelines, we declare their return values as empty interface interface{}, which can represent any type.

Next, let's focus on the implementation of the Reduce function sumAge. Here, we declare its second parameter as a variadic parameter, indicating support for passing multiple processing functions. These processor functions are called in the order they are declared. Since the return value types of these functions are declared as empty interfaces, we need to dynamically check their return value types at runtime and assign them to the specified variables, so that the program can execute as expected without errors due to type issues.

Through pipelines, we can achieve a more elegant flow of Filter->Map->Reduce function chaining. In addition, pipeline technology is widely used in HTTP request handling middleware.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.