art with code

2008-08-24

Prelude.ml

Prelude.ml - my OCaml standard library replacement, started off as a partial port of Haskell's Prelude (which you should read btw, it's very nice) and evolved into something more extensive. It doesn't have the lazy lists -focus of Haskell's, but does things the usual way instead.

The things I tried to make convenient with prelude.ml are functions, lists, strings, type conversions, simple I/O and shell commands.

Here are some examples:


(* Quick vocabulary:
let ($) x f = f x, i.e. it's the reverse of Haskell's $ and the same as |>
[1; 2; 3] $ map (add 3) = [4; 5; 6]

let (@@) f x = f x = Haskell's ($)
map (add 3) @@ [1; 2; 3] = [4; 5; 6]

let (@.) f g x = f (g x) = Haskell's (.)
(add 5 @. multiply 2) 10 = 25

let ($.) f g x = g (f x), the suffix version of (@.)
(add 5 $. multiply 2) 10 = 30
*)

(* let fupler f x = (x, f x) *)
# unfoldr (lte 3) (fupler succ) 1;;
- : int list = [3; 2; 1]

# generateR (lte 3) succ 1;;
- : int list = [3; 2; 1]

# generate (greaterThan 5) pred 10;;
- : int list = [10; 9; 8; 7; 6]

# (10--6);;
- : int list = [10; 9; 8; 7; 6]

(* average file size *)
# readCmd ["ls"; "-l"] $ lines $ tail $ map (words $. nth 4 $. parseInt) $ average;;
- : int = 2920660

(* grepping with scan *)
# readRawCmd "ls /etc/apache2/mods-enabled" $ scan_nth "\\S*python\\S*" 0;;
- : string list = ["mod_python.load"]

(* grepping with filter *)
# ls "/etc/apache2/mods-enabled" $ filter (xmatch "deflate|alias");;
- : string list = ["alias.conf"; "alias.load"; "deflate.conf"; "deflate.load"]

(* most recently modified file *)
# ls "." $ filter isFile $ maximumBy mtime;;
- : string = ".xsession-errors"

(* and with the mtime *)
# ls "." $ filter isFile $ maximumByWith mtime;;
- : string * float = (".xsession-errors", 1220098201.)

(* writing to a file *)
# writeFile "thousand.txt" ( (1--1000) $ map showInt $ join "\n" );;
- : unit = ()

(* file size *)
# fileSize "thousand.txt";;
- : int = 3892

(* reading from a file *)
# readFile "thousand.txt" $ lines $ map parseInt = (1--1000);;
- : bool = true

(* line-wise read *)
# mapLines parseInt "thousand.txt" $ sum;;
- : int = 500500

(* or as an unfold *)
# withFile "thousand.txt" @@ unfoldrOpt (optEOF (fun ic -> readInt ic, ic)) $ sum;;
- : int = 500500

(* let fuple f g x = (f x, g x) *)
# withFile "thousand.txt" @@ unfoldrOpt (optEOF (fuple readInt id)) $ sum;;
- : int = 500500

(* let fuplel f x = (f x, x) *)
# withFile "thousand.txt" @@ unfoldrOpt (optEOF (fuplel readInt)) $ sum;;
- : int = 500500

(* or with tokenizeFile *)
# tokenizeFile readInt "thousand.txt" $ sum;;
- : int = 500500

(* and the idiomatic OCaml way *)
let file = open_in "thousand.txt" in
let rec aux ic sum =
let i = try Some (int_of_string (input_line ic)) with End_of_file -> None in
match i with None -> sum | Some x -> aux ic (x + sum) in
try
let sum = aux file 0 in
( try close_in file with _ -> () );
sum
with e ->
( try close_in file with _ -> () );
raise e
;;
- : int = 500500

(* take 10 . lines =<< readFile "thousand.txt" *)
# tokenizeFileN readLine 10 "thousand.txt";;
- : string list = ["1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "10"]

(* ten-number group averages for the first hundred numbers *)
# tokenizeFileN readFloat 100 "thousand.txt" $ groupsOf 10 $ map averagef;;
- : float list = [5.5; 15.5; 25.5; 35.5; 45.5; 55.5; 65.5; 75.5; 85.5; 95.5]

(* sliding ten-number averages for the first twenty numbers *)
# tokenizeFileN readFloat 20 "thousand.txt" $ mapWindow averagef 10;;
- : float list = [5.5; 6.5; 7.5; 8.5; 9.5; 10.5; 11.5; 12.5; 13.5; 14.5; 15.5]

(* interaction *)
# interact (uppercase @. join "-" @. words);;
hi there
HI-THERE
my name is robby
MY-NAME-IS-ROBBY
i am a robot
I-AM-A-ROBOT
^D
- : unit = ()

What's lacking?
  • I haven't measured performance.
  • It would be nice to have simple socket IO to do network programming.
  • Stream-like things aren't handled like stream-like things: it's a pain to do something like mapWindow that would work for e.g. `tail -f`
  • Lists suck. Arrays are better from hardware POV, but harder to reason about. List-like interface to arrays, maybe? Or ropes of some kind?
  • The OCaml compiler doesn't do beta reduction, so combinators are slow. Could be worked around by turning them into macros.
Post a Comment

Blog Archive

About Me

My photo

Built art installations, web sites, graphics libraries, web browsers, mobile apps, desktop apps, media player themes, many nutty prototypes, much bad code, much bad art.

Have freelanced for Verizon, Google, Mozilla, Warner Bros, Sony Pictures, Yahoo!, Microsoft, Valve Software, TDK Electronics.

Ex-Chrome Developer Relations.