# Julia - Playing with Records - Part Two

Article Series

This article series discuss more than 18 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Table of Content
Where to Discuss?

Local Group

### Preface

Goal: Continue Part One

### 5: Approach in Solving Unique

A custom example, that you can rewrite.

#### An Algorithm Case

Why reinvent the wheel?

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.

#### x:xs Pattern Matching

The original `x:xs` pattern matching is made of two functions:

1. Exclude
2. Unique

Pattern matching in `Julia` required macro, and is not as fancy as `haskell`, or even `OCaml`. But what we have in `Julia` is enough to solve our little problem.

#### Exclude Function

The `exclude` function is just a `filter` with below details:

``````tags = ["rock", "jazz", "rock", "pop", "pop"]

function exclude(value)
tags -> filter(tag -> tag!=value, tags)
end

tags |> exclude("rock") |> println``````

With the result as below `array`:

``````\$ julia 21-exclude.jl
["jazz", "pop", "pop"]``````

#### Recursive Unique Function

With `exclude` function above, we can build `unique` function recursively, as below code:

``````tags = ["rock", "jazz", "rock", "pop", "pop"]

exclude = function(value, tags)
return [ tag for tag in tags if tag != value ]
end

unique = function(tags)
if length(tags) <= 1
return tags
else
tail = unique(exclude(head, tags))
end
end

tags |> unique |> println``````

With the result as below `array`:

``````\$ julia 22-unique.jl
["rock", "jazz", "pop"]``````

Although the syntax looks is, not exactly the same as functional programming in haskell, the algorithm is the same.

#### Install Match Macro

Instead of just conditional, we can utilize third party macro named `match`. First we have to install using `julia` command line.

``````julia> using Pkg

Installing known registries into `~/.julia`
######################################################################## 100.0%
Added registry `General` to `~/.julia/registries/General`
Resolving package versions...
Installed Match â”€ v1.1.0
Updating `~/.julia/environments/v1.5/Project.toml`
[7eb4fadd] + Match v1.1.0
Updating `~/.julia/environments/v1.5/Manifest.toml`
[7eb4fadd] + Match v1.1.0``````

#### Using Match Macro

Then we can use it as below code.

``````using Match

tags = ["rock", "jazz", "rock", "pop", "pop"]

exclude(value, tags) = begin
filter(tag -> tag!=value, tags)
end

unique(tags)::Array{String} = begin
@match tags begin
[]     => []
_      => begin
tail = unique(exclude(head, tags))
end
end
end

tags |> unique |> println``````

With the result as below `array`:

``````\$ julia 23-match.jl
["rock", "jazz", "pop"]``````

The reason that I put `Array{String}` string is, without the type signature we have this result below:

``````\$ julia 23-match.jl
Any["rock", "jazz", "pop"]``````

One more thing, by the time of this article written, I still don’t know how to match with idiomatic `[tail:head]`.

#### Generic Type

Both functions can should capable applied to any type, not just string. It is a good time to apply generic.

``````tags = ["rock", "jazz", "rock", "pop", "pop"]

function exclude(
value::T, tags::Array{T}
)::Array{T} where {T<:Any}
return [ tag for tag in tags if tag != value ]
end

function unique(
tags::Array{T}
)::Array{T} where {T<:Any}
if length(tags) <= 1
return tags
else
tail = unique(exclude(head, tags))
end
end

tags |> unique |> println``````

With the result as below `array`:

``````\$ julia 24-generics.jl
["rock", "jazz", "pop"]``````

#### Custom Utility Module

This would be great if we can reuse, the generic unique function above into its own module.

Instead of two functions, we can merge both into one, to reduce complexity.

``````module MyUtils

export unique

function unique(tags::Array{T})::Array{T} where {T<:Any}
if length(tags) <= 1
return tags
else
excluded = [ tag for tag in tags if tag != head ]
end
end

end``````

#### Apply to Songs

The complete code is as below:

``````using Base.Iterators

include("MyUtils.jl")
include("MySongs.jl")
using .MySongs

[   song.tags for song in getSongs()
if song.tags!=nothing
] |> flatten |> collect |> MyUtils.unique |> println``````

With the result as below `array`:

``````\$ julia 25-songs.jl
["60s", "jazz", "rock", "70s", "pop"]``````

### 6: More About Function

Dive Deeper with Functional Programming

#### Function Composition

We can rewrite the previous code with function composition. This article below show a very nice trick, that you can use of in `Julia`

For practical reason, I flip the function order.

``````function âˆ˜(f, g)
x -> g(f(x))
end``````

Function composition is pretty common in functional world. Now we can compose the function as below:

``````[   song.tags for song in getSongs()
if song.tags!=nothing
] |> (flatten âˆ˜ collect âˆ˜ unique âˆ˜ println)``````

The complete code is as below:

``````using Base.Iterators

include("MySongs.jl")
using .MySongs

function âˆ˜(f, g)
x -> g(f(x))
end

[   song.tags for song in getSongs()
if song.tags!=nothing
] |> (flatten âˆ˜ collect âˆ˜ unique âˆ˜ println)``````

With the result as below `array`:

``````\$ julia 14-composition.jl
["60s", "jazz", "rock", "70s", "pop"]``````

### 7: Concurrency with Channel

`Julia` support `Actor Model` pattern. We can rewrite those process above through channel.

#### The Skeleton

We should be ready for the real demo. This is only consist of one short file.

``````include("MyUtils.jl")
include("MySongs.jl")
using .MySongs

function flatten(messages::Channel) â€¦ end

function walk(messages::Channel) â€¦ end

function run() â€¦ end

run()``````
1. Producer: flatten()
2. Consumer: walk()
3. Program entry point: run()

#### Producer and Consumer

We should prepare two functions:

1. One for `Producer` that `put!` to channel,
2. And the other one for `Consumer` that `take!` from channel.
``````function flatten(messages::Channel)
songs::Array{Song} = getSongs()

for song in songs
if song.tags!=nothing
for tag in song.tags
put!(messages, tag)
end
end
end
end

function walk(messages::Channel)
newtags::Array{String} = []

for tag in messages
push!(newtags, tag)
end

return newtags
end``````

#### Running Both Routine

Pretty short right!

Consider gather both function in `run` entry point.

``````function run()
messages = Channel(flatten)
walk(messages)     |>
MyUtils.unique |>
println
end

run()``````

With the result as below `array`:

``````\$ julia 27-channel.jl
["60s", "jazz", "rock", "70s", "pop"]``````

This is the end of our `Julia` journey in this article. We shall meet again in other article.

### What is Next ðŸ¤”?

Consider continue reading [ GNU R - Playing with Records ].