...

Anonymous function in Go: An Easy Guide with 4 Use Cases

Rate this post

What is an anonymous function in Go?

It is just a normal function without a name. Anonymous functions are also called lambda functions or function literals. They are commonly defined and are being passed around as small, self-contained code blocks.

Anonymous functions are often used in Go for simple tasks such as sorting or filtering a collection of data. They can also be used as closures to capture and manipulate variables in their surrounding environment.

Major use cases of the Anonymous function in Go

  • Pass as value (as an argument to another function)
  • Execute something concurrently
  • Send value on a channel without blocking the caller
  • Function returning function (closure)
Anonymous function in Go

Pass as value (as an argument to another function)

Let us understand with the classic example which  most of us would already be familiar with as  mentioned below:-

package main

import (
	"fmt"
	"sort"
)

type Student struct {
	Name string
	Age  int
}

func main() {
	students := []Student{
		{Name: "Alice", Age: 21},
		{Name: "Bob", Age: 19},
		{Name: "Charlie", Age: 23},
		{Name: "David", Age: 20},
	}

	// Custom comparison function for sorting by age
	ageLess := func(i, j int) bool {
		return students[i].Age < students[j].Age
	}

	// Use sort.Slice with the custom comparison function
	sort.Slice(students, ageLess)

	fmt.Println("Sorted by age:")
	for _, student := range students {
		fmt.Printf("Name: %s, Age: %d\n", student.Name, student.Age)
	}
}

Explanation

Here, ageLess is an anonymous function since its value is a function without a name. It is being passed to sort.Slice (2nd argument). It is sort of lived since its scope is within the main function.
Note: Please install Go to run it locally on your system.

Execute something concurrently

Go anonymous function facilitates concurrent execution of independent tasks.

Let us extend the above example as below:-

After sorting we will find max age of all students and set its value in redis as below:-

package main

import (
	"fmt"
	"sort"
	"sync"

	"github.com/go-redis/redis/v8" // Import the Redis package
	"golang.org/x/net/context"     // Import context for Redis
)

type Student struct {
	Name string
	Age  int
}

func main() {
	students := []Student{
		{Name: "Alice", Age: 21},
		{Name: "Bob", Age: 19},
		{Name: "Charlie", Age: 23},
		{Name: "David", Age: 20},
	}

	// Custom comparison function for sorting by age
	ageLess := func(i, j int) bool {
		return students[i].Age < students[j].Age
	}

	// Use sort.Slice with the custom comparison function
	sort.Slice(students, ageLess)

	// Find the max age
	maxAge := students[len(students)-1].Age

	fmt.Println("Max Age:", maxAge)

	// Update the max age in Redis concurrently
	go updateMaxAgeInRedis(maxAge)
}

func updateMaxAgeInRedis(maxAge int) {
	// Initialize a Redis client
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // Redis server address
		Password: "",             // No password set
		DB:       0,              // Default DB
	})

	// Use a WaitGroup for concurrent goroutines
	var wg sync.WaitGroup
	wg.Add(1)

	// Start a goroutine to update the max age in Redis
	go func() {
		defer wg.Done()

		// Use a context for Redis operations
		ctx := context.Background()

		// Update the max age value in Redis
		err := client.Set(ctx, "maxAge", maxAge, 0).Err()
		if err != nil {
			fmt.Println("Error updating max age in Redis:", err)
		} else {
			fmt.Println("Max age updated in Redis:", maxAge)
		}
	}()

	wg.Wait() // Wait for the goroutine to finish
}

Explanation

We first sort students by age, then we assign the value of the last student’s age as maxAge and finally, we execute updateMaxAgeInRedis concurrently to set value in redis.

Send value on a channel without blocking the caller

Now suppose after calculating we also want to notify on Slack if maxAge is greater than 20.

We can extend the above example and see how this can be done.

package main

import (
	"fmt"
	"sort"
	"sync"

	"github.com/go-redis/redis/v8" // Import the Redis package
	"golang.org/x/net/context"     // Import context for Redis
)

type Student struct {
	Name string
	Age  int
}

var maxAgeChan = make(chan int)

func main() {
	students := []Student{
		{Name: "Alice", Age: 21},
		{Name: "Bob", Age: 19},
		{Name: "Charlie", Age: 23},
		{Name: "David", Age: 20},
	}

	// Custom comparison function for sorting by age
	ageLess := func(i, j int) bool {
		return students[i].Age < students[j].Age
	}

	// Use sort.Slice with the custom comparison function
	sort.Slice(students, ageLess)

	// Find the max age
	maxAge := students[len(students)-1].Age

	fmt.Println("Max Age:", maxAge)

	// Update the max age in Redis concurrently
	go updateMaxAgeInRedis(maxAge)

     // send maxAge value on maxAgeChan concurrently
    go func() {
           maxAgeChan <- maxAge
    }()
}

func updateMaxAgeInRedis(maxAge int) {
	// Initialize a Redis client
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // Redis server address
		Password: "",             // No password set
		DB:       0,              // Default DB
	})

	// Use a WaitGroup for concurrent goroutines
	var wg sync.WaitGroup
	wg.Add(1)

	// Start a goroutine to update the max age in Redis
	go func() {
		defer wg.Done()

		// Use a context for Redis operations
		ctx := context.Background()

		// Update the max age value in Redis
		err := client.Set(ctx, "maxAge", maxAge, 0).Err()
		if err != nil {
			fmt.Println("Error updating max age in Redis:", err)
		} else {
			fmt.Println("Max age updated in Redis:", maxAge)
		}
	}()

	wg.Wait() // Wait for the goroutine to finish
}

func notifyOnSlack() {
	for maxAge := range maxAgeChan {
                  if maxAge <= 20 {
                           continue
                  }
		// Initialize a Slack API client
		api := slack.New("YOUR_SLACK_TOKEN")

		// Set the message text
		messageText := fmt.Sprintf("Max age is %d which is greater than 20!", maxAge)

		// Post the message to the Slack channel
		_, _, err := api.PostMessage("YOUR_CHANNEL_ID", slack.MsgOptionText(messageText, false))
		if err != nil {
			fmt.Println("Error posting message to Slack:", err)
		} else {
			fmt.Println("Message posted to Slack")
		}
	}
}

Explanation

We created a function(notifyOnSlack) that will continuously receive maxAge value on a channel and check if maxAge is greater than 20 then it will notify on a Slack channel.

Function returning function (closure)

Let us understand with the below example of a calculator.

func Add(n float64) func(float64) float64 {
        return func(acc float64) float64 {
                return acc + n
        }
}

func Sub(n float64) func(float64) float64 {
        return func(acc float64) float64 {
                return acc - n
        }
}

func Mul(n float64) func(float64) float64 {
        return func(acc float64) float64 {
                return acc * n
        }
}

func Sqrt() func(float64) float64 {
        return func(n float64) float64 {
                return math.Sqrt(n)
        }
}


func (c *Calculator) Do(op func(float64) float64) float64 {
        c.acc = op(c.acc)
        return c.acc
}
func main() {
        var c Calculator
        c.Do(Add(10))   // 10
        c.Do(Add(20))   // 30
        c.Do(Sub(5)) // 25
       c.Do(Sqrt()) //5
}

Explanation

In the above example, Add, Sub, Mul, and Sqrt return a function having signature func(float64) float64. These returned functions are anonymous functions.
Also, in the case of Add, Sub, and Mul returned functions close n (surrounding value) and serve as closure too.

Conclusion

In conclusion, anonymous functions in Go stand as versatile tools empowering developers to create concise, flexible, and expressive code. These unnamed functions, encapsulating functionality without a declared identifier, provide a powerful means to adapt code dynamically, especially in scenarios requiring ad-hoc or short-lived functionality.

The flexibility afforded by anonymous functions in Go extends to various programming paradigms, enabling the creation of closures, callbacks, and concurrent operations. Their ability to capture and manipulate local variables within their lexical scope enhances code readability and maintains the integrity of the surrounding context.

Anonymous functions exemplify Go’s simplicity and efficiency, allowing developers to write clear and compact code without compromising functionality. Their usage facilitates modular design and cleaner code organization, promoting reusability and maintainability within applications.

Overall, anonymous functions in Go embody the language’s ethos of simplicity, promoting flexibility, readability, and code efficiency, thus serving as valuable assets for crafting robust and adaptable Go programs.

Spread the love

Leave a Comment

Seraphinite AcceleratorOptimized by Seraphinite Accelerator
Turns on site high speed to be attractive for people and search engines.