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.

No comments:

Blog Archive