Preface
Goal: Continue Part One
4: Approach in Solving Unique
A custom pattern matching example, that you can rewrite.
An Algorithm Case
Why making things complex?
My intention is writing a custom pattern matching example, so later I can rewrite other algorithm based on this example. You can read the detail on the first article. The record overview.
Consider going back to simple data. We need to discuss about solving unique list.
Head and Tail Pattern Matching
The original x:xs pattern matching from haskell shown as below:
exclude :: String -> ([String] -> [String])
exclude value = filter((/=) value)
unique :: [String] -> [String]
unique [] = []
unique (head:tail) = head:unique(exclude head tail)
tags :: [String]
tags = ["rock", "jazz", "rock", "pop", "pop"]
main = print (unique tags)With the result of
["rock","jazz","pop"]
This x:xs pattern is made of two functions:
- Exclude
- Unique
Loop for Exclude
Based on
exclude :: String -> ([String] -> [String])
exclude value = filter((/=) valueWe can rewrite twoliners above to this below:
package main
import "fmt"
func exclude(value string, tags []string) []string {
var newTags []string
for _, tag := range tags {
if tag != value {
newTags = append(newTags, tag)
}
}
return newTags
}
func main() {
var tags = []string{
"rock", "jazz", "rock", "pop", "pop"}
fmt.Println(exclude("rock", tags))
}With the result similar as below slices:
$ go run 08-exclude.go
[jazz pop pop]
Loop for Unique
Based on
unique :: [String] -> [String]
unique [] = []
unique (head:tail) = head:unique(exclude head tail)We can rewrite threeliners above to this below:
package main
import "fmt"
func exclude(value string, tags []string) []string {
…
}
func unique(tags []string) []string {
if len(tags) <= 1 {
return tags
} else {
head := tags[0]
tail := tags[1:len(tags)]
newHeads := []string{head}
newTails := unique(exclude(head, tail))
return append(newHeads, newTails...)
}
}
func main() {
var tags = []string{
"rock", "jazz", "rock", "pop", "pop"}
fmt.Println(unique(tags))
}With the result similar as below slices:
$ go run 09-unique.go
[rock jazz pop]
The Recursive
Based on
… = head:unique(exclude head tail)Transformed into
head := tags[0]
tail := tags[1:len(tags)]
newHeads := []string{head}
newTails := unique(exclude(head, tail))
return append(newHeads, newTails...)I guess the code itself is self explanatory.
myutils Package
The code can be shown as below:
package myutils
func exclude(value string, tags []string) []string {
…
}
func Unique(tags []string) []string {
…
}
I keep the exclude function with lowercase,
while changing unique function to uppercase.
Configuration
The go.mod file is simply as below setting
module mysongs
go 1.15And the go.mod file for root directory has some settings:
module tutor
go 1.15
replace example.com/mysongs => ./mysongs
require example.com/mysongs v1.0.0
replace example.com/myutils => ./myutils
require example.com/myutils v1.0.0Clean Up
Now we can have a short and tidy code.
package main
import (
"example.com/mysongs"
"example.com/myutils"
"fmt"
)
func main() {
var tags []string
tags = mysongs.GetSongs().FlattenTags()
tags = myutils.Unique(tags)
fmt.Println(tags)
}With the result similar as below slices:
$ go run 10-cleanup.go
[60s jazz rock 70s pop]

Now you can rewrite your own pattern matching algorithm to go.
5: Concurrency using Goroutine
Before we make a complex goroutine,
we can utilize previous flatten example,
as a show case to pass data between goroutine.
Passing by Reference
To use defer with array of string,
I need to explain pointer a bit.
When we run a goroutine, we need to pass variable as parameter argument in function. In order to change the value of the parameter , we need to pass it by reference.
func flatten(songs []mysongs.Song, tags *[]string) {
…
}And change the value later by uisng pointer.
*tags = append(*tags, tag)The complete code is as below:
package main
import (
"example.com/mysongs"
"fmt"
)
func flatten(songs []mysongs.Song, tags *[]string) {
for _, song := range songs {
if song.Tags != nil {
for _, tag := range song.Tags {
*tags = append(*tags, tag)
}
}
}
}
func main() {
var tags []string
flatten(mysongs.GetSongs(), &tags)
fmt.Println(tags)
}_.
With the result similar as below slices:
$ go run 11-byref.go
[60s jazz 60s rock 70s rock 70s pop]
Waitgroup
Following the official documentation,
we can apply waitgroup for this situation.
Consider start with defer.
package main
import (
"example.com/mysongs"
"fmt"
"sync"
)
func flatten(wg *sync.WaitGroup,
songs []mysongs.Song, tags *[]string) {
defer wg.Done()
for _, song := range songs {
if song.Tags != nil {
for _, tag := range song.Tags {
*tags = append(*tags, tag)
}
}
}
}And apply the waitgroup pattern` in main function:
func main() {
wg := &sync.WaitGroup{}
var tags []string
wg.Add(1)
go flatten(wg, mysongs.GetSongs(), &tags)
wg.Wait()
fmt.Println(tags)
}With exactly the same result as previous slices:
$ go run 12-defer.go
[60s jazz 60s rock 70s rock 70s pop]
Passing Data Between Channel
We need to create two functions.
The flatten itself, and other function to walk the result.
The flatten function is very similar as previous code.
func flatten(message chan string, quit chan int) {
songs := mysongs.GetSongs()
for _, song := range songs {
if song.Tags != nil {
for _, tag := range song.Tags {
message <- tag
}
}
}
quit <- 0
}_.
On the other hand, the walk function is,
following the example from official documentation.
func walk(message chan string, quit chan int) {
var tags []string
var tag string
for {
select {
case tag = <-message:
tags = append(tags, tag)
case <-quit:
fmt.Println(tags)
return
}
}
}Now we can call both functions, in main.
package main
import (
"example.com/mysongs"
"fmt"
)
func walk(message chan string, quit chan int) {…}
func flatten(message chan string, quit chan int) {…}
func main() {
message := make(chan string)
quit := make(chan int)
go flatten(message, quit)
walk(message, quit)
}With exactly the same result as previous slices:
$ go run 13-channel.go
[60s jazz 60s rock 70s rock 70s pop]

What is Next 🤔?
There is however other way to make our life simpler.
We can query using Koazee, go-linq or go-funk.
Consider continue reading [ Go - Playing with Records - Part Three ].