Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learning Functional Programming in Go

You're reading from   Learning Functional Programming in Go Change the way you approach your applications using functional programming in Go

Arrow left icon
Product type Paperback
Published in Nov 2017
Publisher Packt
ISBN-13 9781787281394
Length 670 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Lex Sheehan Lex Sheehan
Author Profile Icon Lex Sheehan
Lex Sheehan
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Pure Functional Programming in Go 2. Manipulating Collections FREE CHAPTER 3. Using High-Order Functions 4. SOLID Design in Go 5. Adding Functionality with Decoration 6. Applying FP at the Architectural Level 7. Functional Parameters 8. Increasing Performance Using Pipelining 9. Functors, Monoids, and Generics 10. Monads, Type Classes, and Generics 11. Category Theory That Applies 12. Miscellaneous Information and How-Tos

The difference between an anonymous function and a closure

Let's look at a few simple code examples to understand the difference between an anonymous function and a closure.

Here's a typical named function:

func namedGreeting(name string) {
fmt.Printf("Hey %s!n", name)
}

The following is an example of the anonymous function:

func anonymousGreeting() func(string) {
return func(name string) {
fmt.Printf("Hey %s!n", name)
}
}

Now, let's call them both and call an anonymous inline function to say Hey to Cindy:

func main() {
namedGreeting("Alice")


greet := anonymousGreeting()
greet("Bob")


func(name string) {
fmt.Printf("Hello %s!n", name)
}("Cindy")
}

The output will be as follows:

Hello Alice!
Hello Bob!
Hello Cindy!

Now, let's look at a closure named greeting and see the difference between it and the anonymousGreeting() function.

Since the closure function is declared in the same scope as the msg variable, the closure has access to it. The msg variable is said to be in the same environment as the closure; later, we'll see that a closure's environment variables and data can be passed around and referenced at a later time during a program's execution:

func greeting(name string) {
msg := name + fmt.Sprintf(" (at %v)", time.Now().String())

closure := func() {
fmt.Printf("Hey %s!n", msg)
}
closure()
}


func main() {
greeting("alice")
}

The output will be as follows:

Hey alice (at 2017-01-29 12:29:30.164830641 -0500 EST)!

In the next example, instead of executing the closure in the greeting() function, we will return it and assign its return value to the hey variable in the main function:

func greeting(name string) func() {
msg := name + fmt.Sprintf(" (at %v)", time.Now().String())
closure := func() {
fmt.Printf("Hey %s!n", msg)
}
return closure
}

func main() {
fmt.Println(time.Now())
hey := greeting("bob")
time.Sleep(time.Second * 10)
hey()
}

The output will be as follows:

2017-01-29 12:42:09.767187225 -0500 EST
Hey bob (at 2017-01-29 12:42:09.767323847 -0500 EST)!

Note that the timestamp is calculated when the msg variable is initialized, at the time the greeting("bob") value is assigned to the hey variable.

So, 10 seconds later, when greeting is called and the closure is executed, it will reference the message that was created 10 seconds ago.

This example shows how closures preserve state. Instead of manipulating the state in the outside environment, closures allow states to be created, passed around, and subsequently referenced.

With functional programming, you still have a state, but it's just passed through each function and is accessible even when the outer scopes, from where they originated, have already exited.

Later in this book, we'll see a more realistic example of how closures can be leveraged to maintain a context of application resources required by an API.

Another way to speed up our recursive Fibonacci function is to use Go's concurrency constructs.

FP using Go's concurrency constructs

Given the expression result := function1() + function2(), parallelization means that we can run each function on a different CPU core and the total time will be approximately the time it takes for the most expensive function to return its result. Consider the following explanation for parallelization and concurrency:

  • Parallelization: Executing multiple functions at the same time (in different CPU cores)
  • Concurrency: Breaking a program into pieces that can be executed independently
I recommend that you check out the video Concurrency is Not Parallelism, by Rob Pike at https://player.vimeo.com/video/49718712. This is where he explains concurrency as a decomposition of a complex problem into smaller components, where individual components can be run simultaneously resulting in improved performance, assuming communication between them is managed.

Go enhances the concurrent execution of Goroutines with synchronization and messaging using channels and provides multiway concurrent control with the Select statement.

The following language constructs provide a model in Go for concurrent software construction that is easy to understand, use, and reason about:

  • Goroutine: A lightweight thread managed by the Go runtime.
  • Go statements: The go instruction that starts the execution of a function call as an independent concurrent thread of control, or Goroutine, in the same address space as the calling code.
  • Channel: A typed conduit through which you can send and receive values with the channel operator, namely <-.

In the following code, data is sent to channel in the first line. In the second line, data is assigned the value received from channel:

channel <- data
data := <-channel

Since Go channels behave as FIFO queues, where the first items in are the first items out, and since the calculation for the next number in a Fibonacci sequence is a small component, it seems that our Fibonacci sequence function calculation is a great candidate for a concurrency implementation.

Let's give it a go. First, let's define a Channel function that uses a channel to perform Fibonacci calculations:

func Channel(ch chan int, counter int) {
n1, n2 := 0, 1
for i := 0; i < counter; i++ {
ch <- n1
n1, n2 = n2, n1 + n2
}
close(ch)
}

First, we declare the variables n1 and n2 to hold our initial sequence values of 0 and 1.

Then, we create a loop for the total number of times given. In each loop, we send the next sequential number to the channel and calculate the next number in the sequence, until we reach our counter value, which is the last sequential number in our sequence.

The following FibChanneled function creates a channel, namely ch, using the make() function and defines it as a channel that contains integers:

func FibChanneled(n int) int {
n += 2
ch := make(chan int)
go Channel(ch, n)
i := 0; var result int
for num := range ch {
result = num
i++
}
return result
}

We run our Channel (Fibonacci) function as a Goroutine and pass it the ch channel and the 8 number, which tells Channel to produce the first eight numbers from the Fibonacci sequence.

Next, we range over the channel and print any values that the channel produces for as long as the channel has not been closed.

Now, let's take a breather and examine what we've accomplished with our Fibonacci sequence examples.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image