content: -- COLUMN ALIGNMENTS ---------------------------------------------------------
  
  property pstrLines : ¬
      "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" & ¬
      "are$delineated$by$a$single$'dollar'$character,$write$a$program\n" & ¬
      "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" & ¬
      "column$are$separated$by$at$least$one$space.\n" & ¬
      "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" & ¬
      "justified,$right$justified,$or$center$justified$within$its$column."
  
  property eLeft : -1
  property eCenter : 0
  property eRight : 1
  
  -- columnsAligned :: EnumValue -> [[String]] -> String
  on columnsAligned(eAlign, lstCols)
      -- padwords :: Int -> [String] -> [[String]]
      script padwords
          on |λ|(n, lstWords)
  
              -- pad :: String -> String
              script pad
                  on |λ|(str)
                      set lngPad to n - (length of str)
                      if eAlign = my eCenter then
                          set lngHalf to lngPad div 2
                          {replicate(lngHalf, space), str, ¬
                              replicate(lngPad - lngHalf, space)}
                      else
                          if eAlign = my eLeft then
                              {"", str, replicate(lngPad, space)}
                          else
                              {replicate(lngPad, space), str, ""}
                          end if
                      end if
                  end |λ|
              end script
  
              map(pad, lstWords)
          end |λ|
      end script
  
      unlines(map(my unwords, ¬
          transpose(zipWith(padwords, ¬
              map(my widest, lstCols), lstCols))))
  end columnsAligned
  
  -- lineColumns :: String -> String -> String
  on lineColumns(strColDelim, strText)
      -- _words :: Text -> [Text]
      script _words
          on |λ|(str)
              splitOn(strColDelim, str)
          end |λ|
      end script
  
      set lstRows to map(_words, splitOn(linefeed, pstrLines))
      set nCols to widest(lstRows)
  
      -- fullRow :: [[a]] -> [[a]]
      script fullRow
          on |λ|(lst)
              lst & replicate(nCols - (length of lst), {""})
          end |λ|
      end script
  
      transpose(map(fullRow, lstRows))
  end lineColumns
  
  -- widest [a] -> Int
  on widest(xs)
      |length|(maximumBy(comparing(my |length|), xs))
  end widest
  
  -- TEST ----------------------------------------------------------------------
  on run
      set lstCols to lineColumns("$", pstrLines)
  
      script testAlignment
          on |λ|(eAlign)
              columnsAligned(eAlign, lstCols)
          end |λ|
      end script
  
      intercalate(return & return, ¬
          map(testAlignment, {eLeft, eRight, eCenter}))
  end run
  
  -- GENERIC FUNCTIONS ---------------------------------------------------------
  
  -- comparing :: (a -> b) -> (a -> a -> Ordering)
  on comparing(f)
      set mf to mReturn(f)
      script
          on |λ|(a, b)
              set x to mf's |λ|(a)
              set y to mf's |λ|(b)
              if x < y then
                  -1
              else
                  if x > y then
                      1
                  else
                      0
                  end if
              end if
          end |λ|
      end script
  end comparing
  
  -- foldl :: (a -> b -> a) -> a -> [b] -> a
  on foldl(f, startValue, xs)
      tell mReturn(f)
          set v to startValue
          set lng to length of xs
          repeat with i from 1 to lng
              set v to |λ|(v, item i of xs, i, xs)
          end repeat
          return v
      end tell
  end foldl
  
  -- Text -> [Text] -> Text
  on intercalate(strText, lstText)
      set {dlm, my text item delimiters} to {my text item delimiters, strText}
      set strJoined to lstText as text
      set my text item delimiters to dlm
      return strJoined
  end intercalate
  
  -- length :: [a] -> Int
  on |length|(xs)
      length of xs
  end |length|
  
  -- Lift 2nd class handler function into 1st class script wrapper
  -- mReturn :: Handler -> Script
  on mReturn(f)
      if class of f is script then
          f
      else
          script
              property |λ| : f
          end script
      end if
  end mReturn
  
  -- map :: (a -> b) -> [a] -> [b]
  on map(f, xs)
      tell mReturn(f)
          set lng to length of xs
          set lst to {}
          repeat with i from 1 to lng
              set end of lst to |λ|(item i of xs, i, xs)
          end repeat
          return lst
      end tell
  end map
  
  -- maximumBy :: (a -> a -> Ordering) -> [a] -> a
  on maximumBy(f, xs)
      set cmp to mReturn(f)
      script max
          on |λ|(a, b)
              if a is missing value or cmp's |λ|(a, b) < 0 then
                  b
              else
                  a
              end if
          end |λ|
      end script
  
      foldl(max, missing value, xs)
  end maximumBy
  
  -- min :: Ord a => a -> a -> a
  on min(x, y)
      if y < x then
          y
      else
          x
      end if
  end min
  
  -- Egyptian multiplication - progressively doubling a list, appending
  -- stages of doubling to an accumulator where needed for binary
  -- assembly of a target length
  
  -- replicate :: Int -> a -> [a]
  on replicate(n, a)
      if class of a is string then
          set out to ""
      else
          set out to {}
      end if
      if n < 1 then return out
      set dbl to a
  
      repeat while (n > 1)
          if (n mod 2) > 0 then set out to out & dbl
          set n to (n div 2)
          set dbl to (dbl & dbl)
      end repeat
      return out & dbl
  end replicate
  
  -- Text -> Text -> [Text]
  on splitOn(strDelim, strMain)
      set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
      set lstParts to text items of strMain
      set my text item delimiters to dlm
      return lstParts
  end splitOn
  
  -- transpose :: [[a]] -> [[a]]
  on transpose(xss)
      script column
          on |λ|(_, iCol)
              script row
                  on |λ|(xs)
                      item iCol of xs
                  end |λ|
              end script
  
              map(row, xss)
          end |λ|
      end script
  
      map(column, item 1 of xss)
  end transpose
  
  -- [Text] -> Text
  on unlines(lstLines)
      intercalate(linefeed, lstLines)
  end unlines
  
  -- [Text] -> Text
  on unwords(lstWords)
      intercalate(" ", lstWords)
  end unwords
  
  -- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
  on zipWith(f, xs, ys)
      set lng to min(length of xs, length of ys)
      set lst to {}
      tell mReturn(f)
          repeat with i from 1 to lng
              set end of lst to |λ|(item i of xs, item i of ys)
          end repeat
          return lst
      end tell
  end zipWith
  