Article Series

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

Playing with Records Related Articles.

Preface

Goal: A brief explanation about learning functional, with a ready to use example records.

As part of my learning journey, I pick a simple algorithm, finding unique array/list from a few records. Then I implement in different language.

I believe this is going to be easier to be understood, from the code/script author, and the reader

Article Series

For each languages I make a dossier of source code, in step by step fashioned extensively. And then I pour the explanation for each steps, resulting in dozens of articles.

Empower Coder Community 🤔?

My self-portrait, pretend that I’m thinking.

I am thinking!

Leverage Hello World

Back to square one.

Starting from zero as beginner. I started all over again as an empty cup. Wider my experience as a ployglot. Never underestimate any major language. Then suddenly, I realize that I know nothing.

This is what I must do decades ago. Frankly speaking, if you are a beginner, you should take similar path, with your own case examples.

Feynman Method

I’m using feynman method.

  • Take notes

  • Explain to others.

My implementation is

  1. Write down my code to a repository as personal notes.

  2. Explain as an article series in a blog.

Material

One problem, Many programming Languages

I limit the material to javascript related language. Because I know, my time is limited. I have works to do.

The ecmascript and typescript itself is, not a functional programming language. But we can improve those two by bringing functional approach, for these two languages.

The article has been made so far is just a few.

  1. Ecmacript (Towards Functional)
  2. Typescript (Small Step into Typed)
  3. Haskell as Purescript Mockup
  4. Purescript
  5. ReasonML
  6. OCaml
  7. F#
  8. Erlang
  9. Elixir
  10. Clojure
  11. Scala
  12. Lua
  13. Julia
  14. GNU R
  15. Nim
  16. Crystal
  17. Go
  18. Rust
  19. C#
  20. Python
  21. Ruby
  22. PHP
  23. BASH, Fish Shell, Ion Shell
  24. JQ, AWK, Sed
  25. Perl 5
  26. Perl 6 (Raku)
  27. TCL/TK
  28. Guile
  29. Racket
  30. Racket

I know there is still other languages, such as elm, groovy, and racket. Also minor language such as idris, agda, coq, unison, odin, jai, poni, squirrel, vlang and brainf++k. But now there are only these, that each are ready to be read.

An Algorithm Case

Why making things complex?

I cannot unseen what I have seen in functional programming, such as x:xs pattern matching algorithm. there are so many algorithm using this pattern, with wide application.

In fact I have made my custom real life problem solving, using this pattern matching. I will pour this specific topic in other article series later. For now we need a simple example, a base for further application.

Luckily this article has a good case to explore with. But some languages has already had built in unique function, that is easy to use with.

Why reinvent the wheel?

I would like to write my own unique function, with just loop and recursive. My intention is writing a custom pattern matching example, so later I can rewrite other algorithm based on this example.

Reading The Code

Although I begin with functional approach, not all problem solved with functional approach. Some language are better written in imperative one.

Sometimes it is hard for beginner to read code, especially functional code. Because we are commonly read imperative code.

You will have better understanding, if you dare to create your own record case, and start manipulating fields, using your own custom code.

The more you write your own custom code, the more you can read other people code.

What do you think?


Common Use Case

The task is simply to get the unique string.

In my experience after years of techical blogging. Most blog has capability, to gather tags from each articles. All gathered tags should finally be gathered as unique tag. This distinct values, will be shown in the special archive pag, called archive by tags, or maybe in sidebar for each articles.

Instead of article in a blog. I have very similar case. Here is my list of Beautiful, and imaginative artwork. Some popular songs from the old decades.

const songs  = [
  { title: "Cantaloupe Island",          tags: ["60s", "jazz"] },
  { title: "Let It Be",                  tags: ["60s", "rock"] },
  { title: "Knockin' on Heaven's Door",  tags: ["70s", "rock"] },
  { title: "Emotion",                    tags: ["70s", "pop"] },
  { title: "The River" }
];

Task

Get the unique tag string

The task is simply to get the unique tag string. Of course with functional approach.

After you finished this task with functional approach, I believe you can apply knowledge that you gain for many different case, related with record field, even with the deep nested one.

Imperative Ecmascript (Procedural)

This used to be written as:

let tagSet = new Set();

// using set feature to collect tag names
songs.forEach(song => {
  if( "tags" in song ) {
    let tags = song.tags;
    console.log(tags);

    for (const tag of tags) {
      tagSet.add(tag);
    }
  }
});

console.log(tagSet);

// normalize to array
let allTags = [...tagSet];

console.log(allTags);

1: Functional Ecmascript

With ES 2019 flatMap. This can be rewritten as:

import songs from "./songs-data.js";

const unique = array => [... new Set(array)];

const allTags = unique(songs
  .filter(song => song.tags)
  .flatMap(song => song.tags)
);
console.log(allTags );

2: Typescript

Or if you wish for detail typed structure, you can add type from above script to below script:

import { Song, songs } from "./songs-data";

type   Unique  = (array: Array<string>) => Array<string>;
const  unique  : Unique = (array) => [... new Set(array)];

type  PickTags = (song: Song) => Array<string>;
const pickTags : PickTags = (song) => song.tags!

const allTags: Array<string> = unique(songs
  .filter(  pickTags )
  .flatMap( pickTags )
);
console.log(allTags );

3: Haskell

I need a mockup before writing to purescript:

import MySongs
import Data.List
import Data.Maybe

unwrapTags :: Tags -> [String]
unwrapTags (Tags tags) = tags

main = print $ nub $ concat 
             $ (map unwrapTags)
             $ catMaybes
             $ (map tags songs)

4: Purescript

And then the purescript itself:

module Step14 where
import Prelude
import Effect.Console (log)
import Data.Array (concat, filter, catMaybes, nub)
import Data.Maybe
import MySongs

main = log $ show $ nub
        $ concat $ catMaybes $ (map _.tags songs)

5: ReasonML

After learning haskell, and purescript1, writing the ReasonML` is become an easy thing to do:

module L = Belt.List
module O = Belt.Option
module S = StdLabels.List

open MySongs
open MyTags

L.map (Songs.lsongs, (lsong) => 
    O.getWithDefault(lsong.Songs.ltags, [])
) |> S.flatten 
  |> Tags.unique 
  |> Array.of_list
  |> Js.log

6: OCaml

After using reasonml for a while, it make sense to step into ocaml.

#use "mysongs.ml";;
#use "mytags.ml";;

let unwrap my_option : tl_tags = 
   match my_option with Some x -> x | None -> [];;

let flatten my_list : string list  = List.fold_left (
   fun acc element -> acc @ element
 ) [] my_list;;

let all_tags my_songs: tl_tags list = List.map (
  fun my_song -> (unwrap my_song.tags)
) my_songs;;

let () = print_endline (
    join_str (unique (flatten (all_tags songs)))
  )

7: F#

According to wikipedia, F# originates from Microsoft Research, Cambridge, UK.

open System
#load "MySongs.fsx"

let unwrap myOption: MySongs.MyTags = 
   match myOption with 
   | Some x -> x
   | None -> []

let mapTagss songs: List<MySongs.MyTags> =
    songs |> List.map (
      fun (song: MySongs.MySong) ->
          (song.Tags |> unwrap )
    )

[<EntryPoint>]
MySongs.songs
  |> mapTagss
  |> List.concat 
  |> List.distinct
  |> fun (tags) ->
     "[" + (tags |> String.concat ", ") + "]"
  |> Console.WriteLine

8: Erlang

Go further with Erlang.

-module(t10_unique).
-import(my_songs, [songs/0]).
-import(lists, [append/1]).
-import(set, [from_list/1, to_list/1]).
-export([show/0]).
-include("my_header.hrl").

unique(List) -> sets:to_list(sets:from_list(List)).

show() ->
  Songs = songs(),
  Tags = [ Head#song.tags || Head <- Songs ],
  io:fwrite("~60p~n", [unique(append(Tags))]).

9: Elixir

After using erlang for a while, it make sense to step into elixir.

import Songs

songs()
  |> Enum.map(& &1.tags)
  |> :lists.append
  |> Enum.uniq
  |> IO.inspect

10: Clojure

The language is fine. But it takes leiningen preparation.

(ns t09-pipe (:gen-class) (:use my-songs))

(defn extract
  [songs]
  (remove nil? (map #(get % :tags) songs))
)

(defn -main [& args]
  (->> songs extract flatten distinct println))

I’m still not sure about clojurescript.

11: Scala

I skip the OOP part.

import mysongs._

object T08 extends App {
  def tagsList(songs: List[Song])
    : List[Option[List[String]]]
    = songs map (song => song.tags)

  println(
    tagsList(MySongs.songs)
      .flatten.flatten.distinct
  )
}

I’m still not sure about scala.js.

12: Lua

Lua capability is somehow limited. This is a good challenge.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'
require 'my-tools'

function exclude(val, tags)
  return normalize(filter(
    function (tag) return tag ~= val end, tags))
end

function unique(tags)
  if type(tags) == 'table' then
    if #tags == 0 then return {} end
    if #tags == 1 then return { head(tags) } end

    return join_tables(
      { head(tags) },
      unique( exclude(head(tags), tail(tags)) )
    )
  end
end

local tags = flatten( clean(extract(songs)) )
print_inspect( unique(tags) )

13: Julia

I think I’m in love with this Julia language.

using Base.Iterators

include("MySongs.jl")
using .MySongs

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

14: GNU R

GNU R has different beauty compared with Julia. We can simply utilize dataframe to data handle records.

library(tibble)
library(purrr)

load("songs.RData")

(dataframe %>% 
  dplyr::filter(.data$tags != "NULL")
)$tags %>% flatten %>% unique %>% paste

15: Nim

Nim is surprisingly very easy, and also fun to work with.

import sequtils, sets, MySongs

songs
  .mapIt(it.tags)
  .foldl(a & b)
  .toOrderedSet
  .toSeq
  .echo

16: Crystal

With Crystal, I just feels like copy paste from Ruby code.

require "./my-songs"

puts songs
  .map { |song| song.tags }
  .select { |tags| tags != [] of String }
  .flatten
  .uniq

17: Go

With Go, we have simple code. Simplicity is a necessity.

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)
}

18: Rust

To code in Rust, we need to understand, the concept of ownership and borrowing.

mod my_songs;
mod my_utils;

fn main() {
    let songs: Vec<my_songs::Song> =
        my_songs::get_songs();

    let tags: Vec<&str> = songs
        .into_iter()
        .filter_map(|song| song.tags)
        .flat_map(|tag_s| tag_s)
        .collect();
       
    println!("{:?}", my_utils::unique(tags))
}

19: C#

With C# 9.0, we have OOP in high level, and FP everywhere else.

using System;
using System.Linq;

class Step09Unique
{
  public void Show() {
    MySongs mySongs = new MySongs();
  
    var tags = (mySongs.songs)
      .Select(song => song.Tags)
      .Where(tags => tags != null)
      .SelectMany(tag => tag)
      .Distinct();

    Console.WriteLine("[{0}]",
      string.Join(", ", tags));
  }
}

20: Python

List comprehension everywhere.

from MySongs import songs

tags = [
  tag for song in songs
  if 'tags' in song
    for tag in song['tags']
]

print(list(set(tags)))

21: Ruby

Different approach, compared to python.

require_relative 'MyStructSongs'
include StructSongs

tagss = SONGS
  .map { |song| song.tags }
  .compact
  .flatten
  .uniq

puts "#{tagss}"

22: PHP

Something commonly see in web development.

<?php
require_once(__DIR__.'/MyClassSongs.php');

$tags =
  array_values(
    array_unique(
      array_merge(
        ... array_map(
          function ($song) {
            return $song->tags; },
          $songs)
  )));

printf("%s\n", json_encode($tags));

23: BASH, Fish Shell, Ion Shell

These three shells is belong to one section.

BASH IFS

source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
tags_fl=$(flatten ${songs[@]})
IFS=' '; read -a tags_flatten <<< "${tags_fl[@]}"

declare -A tags_unique

for tag_item in "${tags_flatten[@]}"; do
  let tags_unique[$tag_item]++
done

tags=(${!tags_unique[@]})

IFS=':'
echo "${tags[*]}"

Fish Shell

#!/usr/bin/env fish
source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
set tags (flatten $songs | sort -u)
string join ':' $tags

Ion Shell

#!/usr/bin/env ion
source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
let tags_fl = $(flatten [@songs])
let tags_un = $(echo $tags_fl | sort -u)
echo $join(@split($tags_un \n) :)

24. JQ, AWK, Sed

These three shells is belong to one section.

JQ

jq -r "(.songs[].tags | \
       select( . != null ))[]" \
    songs.json \
  | sort \
  | uniq \
  | tr '\n' ' '

AWK

#!/bin/awk -f
BEGIN {
  FS="[;,]"
  i = 0
}
{
  gsub(/;[ ]+/,";")
  gsub(/,[ ]+/,",")

  $1=""
  OFS=":"
  $1=$1;
  
  split($0, tags_temp, ":")

  for (j in tags_temp)
    if (j > 1)
      tags[++i] = tags_temp[j]
}
END {
  i=0
  for (t in tags) {
    if ( !exist[tags[t]]++ ) {
      unique[++i] = tags[t]
    }
  }

  ORS=":"
  for (u in unique) {
    if (u == i) ORS="\n"
    print unique[u]
  }
}

Sed

#!/bin/sh
exec sed -E '
/^(.*);(.*)$/!d; s/^(.*);(.*)$/\2/
' "$@" | 
exec sed -E '
:label;N;$!b label
s/(,|  *|\n ?)/ /g;s/^ //g;s/ /\n/g
' | sort -u | 
exec sed -E '
:label;N;$!b label
s/\n/:/g
'

25: Perl 5

Long Live CPAN.

use MySongs;

my %seen;

my @songs_tags = grep {
    !$seen{$_}++
  } map {
    @{ $_->{'tags'} }
  } grep {
    exists($_->{'tags'}) 
  } @MySongs::songs;

say join(":", @songs_tags);

26: Perl 6 (Raku)

The evolved Perl, with list comprehensions.

use MySongs;

my @songs_tags = (
  $_<tags>
  if 'tags' ∈ $_.keys
  for @songs
);

my @tags = @songs_tags
  .List.flat.unique;

join(":", @tags).say;
  • Not scary, with the help of official documentation.

27: TCL/TK

The evolved Perl, with list comprehensions.

lappend auto_path "./"
package require MySongs 1.0

set tags [list]

foreach song $MySongs::Songs {
  if [dict exist $song tags] {
    set tagss [dict get $song tags]
    foreach tag $tagss {
      if {[lsearch $tags $tag] < 0} {
        lappend tags $tag
}}}}

set tagsstr [join $tags ":"];
puts "$tagsstr";

28: Guile

Scheme implementation. Obscure language with exotic syntax.

(define-public (unique tags)
  (if (<= (length tags) 1)
    tags
    (let
      ( (head (car tags)) (tail (cdr tags)) )
      (append (list head)
        (unique (delq head tail)))
)))

29: Racket

Very similar to Guile cousin.

#lang racket
(provide unique-tags)

(define (unique-tags tags)
  (if (<= (length tags) 1)
    tags
    (let ( 
        (head (first tags))
        (tail (rest tags)) )
      (append (list head)
        (unique-tags (remove head tail)))
)))

30: Pascal

No functional stuff.

You can have choices whether pure free pascal, or the object pascal one. Pascal is lower level than other language, so the code is longer than usual. Thus, instead of complee code, I can only give partial stuff as below.

function TSongList3.GetUniqueTags(): TTags;
var
  SL : TStringList;
  ASong  : PSong;
  Tag : ANSIString;
begin
  SL := TStringList.Create;
  SL.Duplicates := dupIgnore;
  SL.Sorted := true;

  for ASong in Self do
    for Tag in ASong^.Tags do
      SL.Add(Tag);

  Result := SL.ToStringArray(0, Count-1);

  SL.Free;
end;

Meet The Jabberwocky

Let’s get it on

Functional is not the beast. Nor math. Functional is the strength to solve an issue, that do not exist in imperative realm.

Why would I overcomplicated task, when in the imperative approach works? Because I need to grow up. I do not use Haskell in any of my real project. But I bring the approach in anywhere else.

Enough with talk. Get your vorpal sword. Time to code.

What is Next 🤔?

Our very first example, the most common used programming language, the ecmascript aka javascript.

Consider continue reading [ Ecmascript - Towards Functional - Part One ].