Skip to content

New concept First-Class Functions #2103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 80 commits into from
Apr 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
ac4731f
Inited concept first class functions
antklim Dec 13, 2021
c26da31
Updated about.md
antklim Dec 16, 2021
850acfe
Updated about.md
antklim Jan 3, 2022
dc3235c
Updated about.md - updated example
antklim Jan 3, 2022
f2fc138
Updated introduction.md
antklim Jan 3, 2022
b729244
Added references to links.json
antklim Jan 4, 2022
1ef6dd8
Updated config.json - better description in blurb field
antklim Jan 4, 2022
9fc65ab
Added first class functions usage examples
antklim Jan 10, 2022
1861a9c
Inited concept exercise
antklim Jan 10, 2022
bb5760b
Inited concept exercise doc files
antklim Jan 10, 2022
b7e3b33
Inited concept exercise design
antklim Jan 11, 2022
bfd5550
Inited exemplar
antklim Jan 11, 2022
573d54d
Inited concept example - expenses
antklim Jan 11, 2022
3a0668a
Expenses exercise added to config
antklim Jan 11, 2022
cdefc20
Expenses exercise drafted
antklim Jan 12, 2022
ff21a63
Added expenses tests
antklim Jan 28, 2022
0759b6f
Added exemplar implementation
antklim Jan 28, 2022
27a5b68
Added content to meta/design
antklim Jan 31, 2022
03499ac
Updated exemplar
antklim Jan 31, 2022
bdb262d
Update target go version
antklim Jan 31, 2022
c7eb3e1
Updated exemplar
antklim Jan 31, 2022
c669f6c
Updated exemplar
antklim Jan 31, 2022
b07f451
Added Records type
antklim Jan 31, 2022
c01f236
Docs hints content added
antklim Jan 31, 2022
1b29ae8
Updated exemplar comments
antklim Feb 10, 2022
80e01ec
Added more hints
antklim Feb 10, 2022
9a9d862
Hints prepared for release
antklim Feb 10, 2022
2ac975c
Expenses API refactoring
antklim Feb 11, 2022
8cf22b5
Instructions WIP
antklim Feb 11, 2022
81b318a
Instructions WIP
antklim Feb 11, 2022
fb87cc6
Added check for non-positive N in TopCategoriesN
antklim Feb 12, 2022
7919c89
Added top N categories sorting by name
antklim Feb 12, 2022
bad04fd
Added examples to instructions
antklim Feb 12, 2022
6ebc6fa
Updated example
antklim Feb 12, 2022
9657ab8
Category expenses instructions added
antklim Feb 14, 2022
0a6bf83
Grammar updates in instructions
antklim Feb 14, 2022
445cdb1
Unknown categroy error updated
antklim Feb 14, 2022
228f2ed
Refactoring - Period renamed to DatePeriod
antklim Feb 14, 2022
38130ef
Added docs introduction
antklim Feb 15, 2022
fed7e90
Anonymous function information added
antklim Feb 17, 2022
2ddd0c7
Updated concept introduction
antklim Feb 17, 2022
bfbe7ec
Updated concept about
antklim Feb 17, 2022
3b65c58
Updated test cases
antklim Feb 17, 2022
b1b20e2
Minor changes
antklim Feb 17, 2022
319f79d
Merge pull request #1 from antklim/feat/first-class-functions-concept
antklim Feb 17, 2022
c74e062
Update concepts/first-class-functions/about.md
antklim Mar 1, 2022
68e9d52
Update concepts/first-class-functions/.meta/config.json
antklim Mar 1, 2022
d8e616b
Table tests cases run as separate tests
antklim Mar 1, 2022
a11f43a
Merge branch 'main' of github.com:antklim/go into main
antklim Mar 1, 2022
4abe247
Removed TopCategoriesN task
antklim Mar 1, 2022
cbf38d8
Time creation/parse options added to instructions
antklim Mar 2, 2022
20474ae
Simplified introduction example
antklim Mar 7, 2022
7a06d7c
Updated concept intro
antklim Mar 7, 2022
c0f2af6
Exercise clean-up
antklim Mar 7, 2022
b84c2c9
Added tasks for filter and predicate functions
antklim Mar 8, 2022
09bf153
Filter tests added
antklim Mar 8, 2022
c114193
Minor changes
antklim Mar 8, 2022
d8f2634
Minor changes
antklim Mar 8, 2022
348188b
Update exercises/concept/expenses/.meta/design.md
antklim Mar 27, 2022
b81b9f2
Update concepts/first-class-functions/about.md
antklim Mar 27, 2022
a3c55ff
Update exercises/concept/expenses/expenses.go
antklim Mar 27, 2022
ea38918
Update exercises/concept/expenses/expenses.go
antklim Mar 27, 2022
e45232e
Update exercises/concept/expenses/expenses.go
antklim Mar 27, 2022
86ca4ac
Update exercises/concept/expenses/expenses.go
antklim Mar 27, 2022
03b96c2
Update exercises/concept/expenses/expenses.go
antklim Mar 27, 2022
1708ec0
Merge branch 'exercism:main' into main
antklim Mar 28, 2022
fdb05d8
Separate text and examples with an empty line
antklim Mar 28, 2022
8009ede
Updated data structures comments
antklim Mar 28, 2022
8896a79
Updated data structures comments
antklim Mar 28, 2022
098651a
Removed named returns in Filter
antklim Mar 28, 2022
4e42dad
Added notes about closure
antklim Mar 29, 2022
b87af49
Dates period replaced with days period
antklim Mar 29, 2022
b395fe7
Hints updated
antklim Mar 29, 2022
dab7d67
Updated instructions
antklim Mar 29, 2022
73896ba
Updated expenses introduction
antklim Mar 29, 2022
ff2270b
Merge branch 'main' into main
antklim Mar 29, 2022
fe2ba2b
Updated authors and contributors
antklim Mar 29, 2022
3ad86a0
Merge branch 'main' of github.com:antklim/go into main
antklim Mar 29, 2022
5e151fd
Added closure link to concept exercise introduction
antklim Mar 29, 2022
85c04fb
Final touches to the first-class function exercise
andrerfcsantos Apr 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions concepts/first-class-functions/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"authors": ["antklim"],
"contributors": ["andrerfcsantos"],
"blurb": "Functions in Go can be used as regular values of the language."
}
103 changes: 103 additions & 0 deletions concepts/first-class-functions/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# About

In Go, functions are first-class values. This means that you can do with functions the same things you can do with all other values - assign functions to variables, pass them as arguments to other functions or even return functions from other functions.

Below we are creating two functions, `engGreeting` and `espGreeting` and we are assigning them to the variable `greeting`:

```go
import "fmt"

func engGreeting(name string) string {
return fmt.Sprintf("Hello %s, nice to meet you!", name)
}

func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}

greeting := engGreeting // greeting is a variable of type func(string) string
fmt.Println(greeting("Alice")) // Hello Alice, nice to meet you!

greeting = espGreeting
fmt.Println(greeting("Alice")) // ¡Hola Alice, mucho gusto!
```

Function values provide an opportunity to parametrize functions not only with data but with behavior too.
In the following example, we are passing behaviour to the `dialog` function via the `greetingFunc` parameter:

```go
func dialog(name string, greetingFunc func(string) string) {
fmt.Println(greetingFunc(name))
fmt.Println("I'm a dialog bot.")
}

func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}

greeting := engGreeting
dialog("Alice", greeting)
// Output:
// ¡Hola Alice, mucho gusto!
// I'm a dialog bot.
```

The value of an uninitialized variable of function type is `nil`.
Therefore, calling a `nil` function value causes a panic.

```go
var dutchGreeting func(string) string
dutchGreeting("Alice") // panic: call of nil function
```

Function values can be compared with `nil`. This can be useful to avoid unnecessary program panics.

```go
var dutchGreeting func(string) string
if dutchGreeting != nil {
dutchGreeting("Alice") // safe to call dutchGreeting
}
```

## Function types

Using function values is possible thanks to the function types in Go. A function type denotes the set of all functions with the same sequence of parameter types and the same sequence of result types. User-defined types can be declared on top of function types. For instance, the `dialog` function from the previous examples can be updated as following:

```go
type greetingFunc func(string) string

func dialog(name string, f greetingFunc) {
fmt.Println(f(name))
fmt.Println("I'm a dialog bot.")
}
```

## Anonymous functions

Another powerful tool that is available thanks to first-class functions support is anonymous functions. Anonymous functions are defined at their point of use, without a name following the `func` keyword. Such functions have access to the variables of the enclosing function.

For example:

```go
func fib() func() int {
var n1, n2 int

return func() int {
if n1 == 0 && n2 == 0 {
n1 = 1
} else {
n1, n2 = n2, n1 + n2
}
return n2
}
}

next := fib()
for i := 0; i < N; i++ {
fmt.Printf("F%d\t= %4d\n", i, next())
}
```

A call to `fib` initializes the variables `n1` and `n2` and returns an anonymous function that, in turn, changes the values of these variables each time the function is called. Nth calls of the anonymous function return the Nth number of the Fibonacci sequence starting from 0. The anonymous inner function has access to the local variables (`n1` and `n2`) of the enclosing function `fib`. This is a great way to have function values keep state between calls. We say that the anonymous function is a closure of the variables `n1` and `n2`. [Closures][closure] are widely used in programming and you might see other languages supporting them.

[closure]: https://en.wikipedia.org/wiki/Closure_(computer_programming)
103 changes: 103 additions & 0 deletions concepts/first-class-functions/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Introduction

In Go, functions are first-class values. This means that you can do with functions the same things you can do with all other values - assign functions to variables, pass them as arguments to other functions or even return functions from other functions.

Below we are creating two functions, `engGreeting` and `espGreeting` and we are assigning them to the variable `greeting`:

```go
import "fmt"

func engGreeting(name string) string {
return fmt.Sprintf("Hello %s, nice to meet you!", name)
}

func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}

greeting := engGreeting // greeting is a variable of type func(string) string
fmt.Println(greeting("Alice")) // Hello Alice, nice to meet you!

greeting = espGreeting
fmt.Println(greeting("Alice")) // ¡Hola Alice, mucho gusto!
```

Function values provide an opportunity to parametrize functions not only with data but with behavior too.
In the following example, we are passing behaviour to the `dialog` function via the `greetingFunc` parameter:

```go
func dialog(name string, greetingFunc func(string) string) {
fmt.Println(greetingFunc(name))
fmt.Println("I'm a dialog bot.")
}

func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}

greeting := engGreeting
dialog("Alice", greeting)
// Output:
// ¡Hola Alice, mucho gusto!
// I'm a dialog bot.
```

The value of an uninitialized variable of function type is `nil`.
Therefore, calling a `nil` function value causes a panic.

```go
var dutchGreeting func(string) string
dutchGreeting("Alice") // panic: call of nil function
```

Function values can be compared with `nil`. This can be useful to avoid unnecessary program panics.

```go
var dutchGreeting func(string) string
if dutchGreeting != nil {
dutchGreeting("Alice") // safe to call dutchGreeting
}
```

## Function types

Using function values is possible thanks to the function types in Go. A function type denotes the set of all functions with the same sequence of parameter types and the same sequence of result types. User-defined types can be declared on top of function types. For instance, the `dialog` function from the previous examples can be updated as following:

```go
type greetingFunc func(string) string

func dialog(name string, f greetingFunc) {
fmt.Println(f(name))
fmt.Println("I'm a dialog bot.")
}
```

## Anonymous functions

Another powerful tool that is available thanks to first-class functions support is anonymous functions. Anonymous functions are defined at their point of use, without a name following the `func` keyword. Such functions have access to the variables of the enclosing function.

For example:

```go
func fib() func() int {
var n1, n2 int

return func() int {
if n1 == 0 && n2 == 0 {
n1 = 1
} else {
n1, n2 = n2, n1 + n2
}
return n2
}
}

next := fib()
for i := 0; i < N; i++ {
fmt.Printf("F%d\t= %4d\n", i, next())
}
```

A call to `fib` initializes the variables `n1` and `n2` and returns an anonymous function that, in turn, changes the values of these variables each time the function is called. Nth calls of the anonymous function return the Nth number of the Fibonacci sequence starting from 0. The anonymous inner function has access to the local variables (`n1` and `n2`) of the enclosing function `fib`. This is a great way to have function values keep state between calls. We say that the anonymous function is a closure of the variables `n1` and `n2`. [Closures][closure] are widely used in programming and you might see other languages supporting them.

[closure]: https://en.wikipedia.org/wiki/Closure_(computer_programming)
14 changes: 14 additions & 0 deletions concepts/first-class-functions/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"url": "https://go.dev/ref/spec#Function_types",
"description": "Go Language Spec: Function types"
},
{
"url": "https://go.dev/tour/moretypes/24",
"description": "Tour of Go: Function values"
},
{
"url": "https://golangbot.com/first-class-functions/",
"description": "Golang tutorial: First Class Functions"
}
]
20 changes: 20 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,21 @@
],
"status": "beta"
},
{
"slug": "expenses",
"name": "Expenses",
"uuid": "7221b17b-7880-4228-a07a-121e5857c953",
"concepts": [
"first-class-functions"
],
"prerequisites": [
"for-loops",
"functions",
"structs",
"type-definitions"
],
"status": "beta"
},
{
"slug": "animal-magic",
"name": "Animal magic",
Expand Down Expand Up @@ -1957,6 +1972,11 @@
"slug": "functions",
"uuid": "e737f47b-5d1a-423a-8d6a-cd201a6e4e44"
},
{
"name": "First class functions",
"slug": "first-class-functions",
"uuid": "66968174-701b-484c-a84d-f919b4756022"
},
{
"name": "Floating-point numbers",
"slug": "floating-point-numbers",
Expand Down
31 changes: 31 additions & 0 deletions exercises/concept/expenses/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Hints

## 1. Implement a general records filter

- Create a new `[]Record` to hold the filtered results
- Iterate over the records and call the predicate function on each one
- Add to the results slice only the records for which calling the predicate function on them returns `true`

## 2. Filter records within a period of time

- Make the function return an anonymous function that receives a `Record` and returns a `bool` indicating if the record is within the period of time that `ByDaysPeriod` receives as an argument.
- Inside your anonymous function, you have access to the `DaysPeriod` argument that `ByDaysPeriod` receives. Together with the `Record` argument of the anonymous function, make a boolean expression that checks if the day in the record is inside the `DaysPeriod` that `ByDaysPeriod` receives. Return that boolean expression from the anonymous function.

## 3. Filter records by category

- This is very similar to the previous task, but now you are dealing with a category instead of a period of time.
- Make the function return an anonymous function that receives a `Record` and returns a `bool` indicating if the record belongs to the category that `ByCategory` receives as an argument.
- Inside your anonymous function, you have access to the category argument that `ByCategory` receives. Together with the `Record` argument of the anonymous function, make a boolean expression that checks if the category of the record is the same as the category that `ByCategory` receives as an argument. Return that boolean expression from the anonymous function.

## 4. Calculate the total amount of expenses in a period

- Use the `ByDaysPeriod` function you made in step 2 to make a period filter function.
- Pass that filter function as the second argument of the `Filter` function you made in step 1. This allows you to filter the results by a period of time.
- Iterate over the filtered records and sum up all their amounts.

## 5. Calculate the total expenses for records of a category in a period

- Use the `ByCategory` function you made in step 3 to make a category filter function.
- Pass that filter function as the second argument of the `Filter` function you made in step 1. This allows you to filter the results by a particular category.
- After filtering the records by category, check if you have any records for thatcategory. If you don't, you must return an error.
- If you have records belonging to that category, compute the total expenses for the given period of time using the `TotalByPeriod` function you made in step 4.
Loading