Nanyx

CSV from a table literal

A table literal of records written out as CSV, then read back and parsed with proc.

module csv

import (
  nanyx/io
  nanyx/math
)

type Stock = (ticker: string, name: string, price: float, change: float, pct: float)

-- A table literal is a list of records with the same shape
def stocks: list(Stock) =
  [| ticker | name                  | price  | change | pct     |
   | "GOOG" | "Google, Inc."        | 505.24 | 0.47   | 0.09    |
   | "YHOO" | "Yahoo! Inc."         | 27.38  | 0.33   | 1.22    |
   | "CNET" | "CNET Networks, Inc." | 8.62   | -0.13  | -1.4901 |]

def quote: string -> string = { s -> '"' ++ s ++ '"' }

def toCsvLine: Stock -> string = { s ->
  [s.ticker, quote(s.name), "{s.price}", "{s.change}", "{s.pct}"] \String.join(",")
}

def fromCsvLine: string -> #some(Stock) | #error(#malformed | #notANumber) = { line ->
  match line \split(",")
    | [ticker, name, price, change, pct] -> proc {
        return Stock(
          ticker = ticker
          name = name \trim('"')
          price = try Float.parse(price)
          change = try Float.parse(change)
          pct = try Float.parse(pct)
        )
      }
    | _ -> #error(#malformed)
}

def statusLabels = [-1 => "down", 0 => "unchanged", 1 => "up"]

@entry
def main: [Console & FileSystem] () -> () = {
  def path = "stocks.csv"

  -- Write the stocks as comma-separated values
  File.writeLines(path, stocks \map(toCsvLine))

  -- Read them back and print a status line for each
  for File.readLines(path) | line ->
    match fromCsvLine(line)
      | #some(stock) ->
        def #some(status) = statusLabels(Math.sign(stock.change)) except #notFound -> #some("unknown")
        println("{stock.name} is {status} {stock.pct}")
      | #error(_) ->
        println("Skipping malformed line: {line}")
}

This program lives in the repository as examples/csv.nyx.