Monday, December 5, 2022

Do-notation for chains of transformations

 A particular idiom kept appearing in Egel since I started using `|>` to form chains of transformations, I kept introducing an abstraction to set the chain up with an unknown initial argument.

I pondered on it for a while and introduced `do` syntactic sugar into Egel. The semantics of `(do f |> g |> h) x` is `x |> f |> g |> h` for example. That allows one to abstract from superfluous variables.

It works.  Below, an example taken from Advent of Code '22.

# Advent of Code (AoC) - day 5, task 2

import "prelude.eg"
import "os.ego"
import "regex.ego"

using System
using OS
using List

def input =
    let L = read_line stdin in if eof stdin then {} else {L | input}

val digits = Regex::compile "[0-9]+"

def parse_crates = 
    do map (do unpack |> chunks 4 |> map (nth 1)) 
    |> transpose |> map (filter ((/=) ' '))
def parse_moves = 
    map (do Regex::matches digits |> map to_int 
         |> [{M,F,T} -> (M, F, T)])

def move =
    [(CC,MM) -> foldl 
        [CC (N,F,T) ->
            CC |> insert (T - 1) 
                   (take N (nth (F - 1) CC) ++ nth (T - 1) CC) 
               |> insert (F - 1) 
                   (drop N (nth (F - 1) CC))]
        CC MM ]

def main =
    input |> break ((==) "") 
          |> [(CC,MM) -> (parse_crates (init CC), parse_moves (tail MM))]
          |> move |> map head |> pack

  

I am not sold on the donation, the abstraction also works as a strong visual reminder that a function is being expressed. But maybe it takes some getting used to. Also, it's a nice pun on Haskell monads and a reference to an old thought of mine that all you should need is function composition to chain actions, and that later became applicatives.