option monad, and some design changes
This commit is contained in:
parent
1c94d51334
commit
de837bbf3b
@ -12,6 +12,7 @@ This library includes the following custom modules:
|
||||
|
||||
- Int (exposure of basic integer arithmetic functions)
|
||||
- Float (exposure of basic float arithmetic functions)
|
||||
- Option (functions for working with the option monad)
|
||||
- List (functional list data structure)
|
||||
- Queue (functional queue implemented as two lists)
|
||||
- Set (functional set implemented as a red-black tree)
|
||||
@ -21,13 +22,13 @@ This library includes the following custom modules:
|
||||
|
||||
## Exposure of Functions from Standard Library
|
||||
|
||||
With respect to the exposed parts of the standard library, these are all handled in the `FromStdlib` module, which redefines some definitions directly from the standard library so that this file can be safely included separately, exposing only the desired functions. As such, it is recommended that this file is opened in the code that uses this library, while others are not, and referenced from the module level instead (with one additional exception of `Types`, mentioned in the following section).
|
||||
With respect to the exposed parts of the standard library, these are all handled in the `FromStdlib` module, which redefines some definitions directly from the standard library so that this file can be safely included separately, exposing only the desired functions. As such, it is recommended that this file is opened in the code that uses this library, while others are not, and referenced from the module level instead (with one additional exception of `Exposed`, mentioned in the following section).
|
||||
|
||||
All files are compiled with `-nopervasives` except `FromStdlib` (to avoid the headaches in exposing functions like `printf` which have many dependencies). Linking is also done without `-nopervasives` so that `fromStdlib.cmx` can find the corresponding functions. Hence any new files added to the project are recommended to be compiled separately with `nopervasives` and then linked via the `.cmx` file.
|
||||
|
||||
## Type Declarations
|
||||
## Type Declarations and General Functions
|
||||
|
||||
In order to prevent duplicate definitions of common types like collections, but still allow things like list literals to work, and to prevent the need of a type annotation at the module level, a `Types` module is provided to be opened in code files which exposes types like `'a queue` and other collections.
|
||||
In order to prevent duplicate definitions of common types like collections, but still allow things like list literals to work, and to prevent the need of a type annotation at the module level, a `Exposed` module is provided to be opened in code files which exposes types like `'a queue` and other collections. `Exposed` also includes generic operators that should be opened file wide, such as function composition (`>>`).
|
||||
|
||||
## Build Process
|
||||
|
||||
@ -46,4 +47,4 @@ Also take note of the fact that I typically compile everything with `-S` and `-O
|
||||
|
||||
## The Core Library
|
||||
|
||||
One of the unfortunate consequences of the way OCaml's compilation works, is that there is a library called the core library, documented [here](https://ocaml.org/manual/core.html), which contains some definitions for types and exceptions, yet does not include the code from the stdlib that uses them. When compiling with the `-nopervasives` flag, this is still included but without the standard library. While this makes sense from the perspective of having some fundamental exceptions always available, having types like `list` included makes it very annoying when implemented a custom standard library. This quirk is why my library has no type definition for `list`, `bool`, `option`, etc. but still uses these types.
|
||||
One of the unfortunate consequences of the way OCaml's compilation works, is that there is a library called the core library, documented [here](https://ocaml.org/manual/core.html), which contains some definitions for types and exceptions, yet does not include the code from the stdlib that uses them. When compiling with the `-nopervasives` flag, this is still included but without the standard library. While this makes sense from the perspective of having some fundamental exceptions always available, having types like `list` included makes it very annoying when implemented a custom standard library. This quirk is why my library has no type definition for `list`, `bool`, `option`, etc. but still uses these types.
|
@ -10,4 +10,11 @@ type 'a stack =
|
||||
|
||||
type 'a tree =
|
||||
| Leaf
|
||||
| Branch of 'a * 'a tree list
|
||||
| Branch of 'a * 'a tree list
|
||||
|
||||
|
||||
let id (x : 'a) = x
|
||||
|
||||
let ( >> ) f g x = g (f x)
|
||||
|
||||
let ( << ) g f x = g (f x)
|
@ -13,4 +13,13 @@ type 'a stack =
|
||||
(* A purely functional tree with arbitrarily many branches at each node. *)
|
||||
type 'a tree =
|
||||
| Leaf
|
||||
| Branch of 'a * 'a tree list
|
||||
| Branch of 'a * 'a tree list
|
||||
|
||||
(** Identity function. *)
|
||||
val id : 'a -> 'a
|
||||
|
||||
(** Function composition. (f >> g) x represents g (f x). *)
|
||||
val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
|
||||
|
||||
(** Function composition. (f << g) x represents f (g x). *)
|
||||
val ( << ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
|
@ -1,5 +0,0 @@
|
||||
let id (x : 'a) = x
|
||||
|
||||
let ( >> ) f g x = g (f x)
|
||||
|
||||
let ( << ) g f x = g (f x)
|
@ -1,8 +0,0 @@
|
||||
(** Identity function. *)
|
||||
val id : 'a -> 'a
|
||||
|
||||
(** Function composition. (f >> g) x represents g (f x). *)
|
||||
val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
|
||||
|
||||
(** Function composition. (f << g) x represents f (g x). *)
|
||||
val ( << ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
|
@ -1,5 +1,5 @@
|
||||
open FromStdlib
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
let empty : 'a list = []
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
(** The empty list *)
|
||||
val empty : 'a list
|
||||
|
@ -1,3 +1,3 @@
|
||||
open FromStdlib open Types
|
||||
open FromStdlib open Exposed
|
||||
|
||||
let _ = printf "Hello, World\n"
|
@ -4,14 +4,13 @@ build:
|
||||
ocamlopt -i fromStdlib.ml > fromStdlib.mli
|
||||
ocamlopt -S -O3 -c fromStdlib.mli fromStdlib.ml
|
||||
|
||||
# types for other modules separated to easily expose without module reference in projects
|
||||
ocamlopt -S -O3 -nopervasives -c types.mli types.ml
|
||||
# exposed types and functions, that can be opened module wide
|
||||
ocamlopt -S -O3 -nopervasives -c exposed.mli exposed.ml
|
||||
|
||||
# the following files make up the core custom standard library code
|
||||
ocamlopt -S -O3 -nopervasives -c int.mli int.ml
|
||||
ocamlopt -S -O3 -nopervasives -c float.mli float.ml
|
||||
ocamlopt -S -O3 -nopervasives -c float.mli float.ml
|
||||
ocamlopt -S -O3 -nopervasives -c functions.mli functions.ml
|
||||
ocamlopt -S -O3 -nopervasives -c option.mli option.ml
|
||||
ocamlopt -S -O3 -nopervasives -c stack.mli stack.ml
|
||||
ocamlopt -S -O3 -nopervasives -c list.mli list.ml
|
||||
ocamlopt -S -O3 -nopervasives -c map.mli map.ml
|
||||
@ -24,7 +23,7 @@ build:
|
||||
ocamlopt -S -O3 -nopervasives -c main.mli main.ml
|
||||
|
||||
# after all files are individually compiled with -nopervasives, this is compiled with it so that fromStdlib has the necessary linking
|
||||
ocamlopt -S -O3 fromStdlib.cmx types.cmx int.cmx float.cmx functions.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx main.cmx -o program
|
||||
ocamlopt -S -O3 fromStdlib.cmx exposed.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx main.cmx -o program
|
||||
|
||||
# clean removes all except source files. autogenerated mli files are also removed.
|
||||
clean:
|
||||
|
23
lib/option.ml
Normal file
23
lib/option.ml
Normal file
@ -0,0 +1,23 @@
|
||||
open Exposed
|
||||
|
||||
let return (x : 'a) : 'a option = Some x
|
||||
|
||||
let ( ~= ) = return
|
||||
|
||||
let bind (x : 'a option) (f : 'a -> 'b option) : 'b option =
|
||||
match x with
|
||||
| None -> None
|
||||
| Some a -> f a
|
||||
|
||||
let ( >>= ) = bind
|
||||
|
||||
let binary_operator (x : 'a option) (y : 'b option) (f : 'a -> 'b -> 'c) : 'c option =
|
||||
x >>= (fun a -> y >>= (fun b -> ~= (f a b)))
|
||||
|
||||
let map (x : 'a option) (f : 'a -> 'b) : 'b option =
|
||||
x >>= (fun a -> ~= (f a))
|
||||
|
||||
let compose (f : 'a -> 'b option) (g : 'b -> 'c option) : 'a -> 'c option =
|
||||
(fun x -> f x >>= g)
|
||||
|
||||
let ( >=> ) = compose
|
23
lib/option.mli
Normal file
23
lib/option.mli
Normal file
@ -0,0 +1,23 @@
|
||||
(* Monadic return for option type. Trivially boxes the variable of type 'a into a 'a option by applying the Some constructor. *)
|
||||
val return : 'a -> 'a option
|
||||
|
||||
(* Monadic return for option type. Trivially boxes the variable of type 'a into a 'a option by applying the Some constructor. *)
|
||||
val ( ~= ) : 'a -> 'a option
|
||||
|
||||
(* Monadic bind for option type. Passes the 'a stored within the 'a option through the supplied function. If the first argument is None, the result is None. *)
|
||||
val bind : 'a option -> ('a -> 'b option) -> 'b option
|
||||
|
||||
(* Monadic bind for option type. Passes the 'a stored within the 'a option through the supplied function. If the first argument is None, the result is None. *)
|
||||
val ( >>= ) : 'a option -> ('a -> 'b option) -> 'b option
|
||||
|
||||
(* Binary operator, applied to the variables of type 'a and 'b respectively in the first two arguments. *)
|
||||
val binary_operator : 'a option -> 'b option -> ('a -> 'b -> 'c) -> 'c option
|
||||
|
||||
(* Maps the supplied function to the 'a within the first argument. *)
|
||||
val map : 'a option -> ('a -> 'b) -> 'b option
|
||||
|
||||
(* Monadic function composition. *)
|
||||
val compose : ('a -> 'b option) -> ('b -> 'c option) -> 'a -> 'c option
|
||||
|
||||
(* Monadic function composition. *)
|
||||
val ( >=> ) : ('a -> 'b option) -> ('b -> 'c option) -> 'a -> 'c option
|
@ -1,5 +1,5 @@
|
||||
open List
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
let enqueue (a : 'a) (qu : 'a queue) : 'a queue =
|
||||
{ qu with front = a :: qu.back }
|
||||
|
@ -1,4 +1,4 @@
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
(** Adds an element to the back of the queue, returning the new queue. Runs in O(1). *)
|
||||
val enqueue : 'a -> 'a queue -> 'a queue
|
||||
|
@ -1,5 +1,5 @@
|
||||
open FromStdlib
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
let pop (st : 'a stack) : 'a option * 'a stack =
|
||||
match st with
|
||||
|
@ -1,4 +1,4 @@
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
(** Removes the top element from the stack, returning a tuple of the new stack, and None if the stack was empty, or Some [x] if [x] was on top of the stack. Popping an empty stack will result in the returned stack also being empty. Runs in O(1). *)
|
||||
val pop : 'a stack -> 'a option * 'a stack
|
||||
|
@ -1,5 +1,5 @@
|
||||
open FromStdlib
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree =
|
||||
Branch (topBranch, tr1 :: tr2 :: [])
|
||||
|
@ -1,4 +1,4 @@
|
||||
open Types
|
||||
open Exposed
|
||||
|
||||
(* Combines two trees of the same type, with the specified value at the new top node. Runs in O(1). *)
|
||||
val combine : 'a tree -> 'a tree -> 'a -> 'a tree
|
||||
|
Loading…
Reference in New Issue
Block a user