option monad, and some design changes

This commit is contained in:
Aaron Manning 2021-12-19 08:08:10 +11:00 committed by aaron-jack-manning
parent 1c94d51334
commit de837bbf3b
17 changed files with 82 additions and 33 deletions

View File

@ -12,6 +12,7 @@ This library includes the following custom modules:
- Int (exposure of basic integer arithmetic functions) - Int (exposure of basic integer arithmetic functions)
- Float (exposure of basic float arithmetic functions) - Float (exposure of basic float arithmetic functions)
- Option (functions for working with the option monad)
- List (functional list data structure) - List (functional list data structure)
- Queue (functional queue implemented as two lists) - Queue (functional queue implemented as two lists)
- Set (functional set implemented as a red-black tree) - 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 ## 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. 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 ## Build Process
@ -46,4 +47,4 @@ Also take note of the fact that I typically compile everything with `-S` and `-O
## The Core Library ## 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.

View File

@ -10,4 +10,11 @@ type 'a stack =
type 'a tree = type 'a tree =
| Leaf | 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)

View File

@ -13,4 +13,13 @@ type 'a stack =
(* A purely functional tree with arbitrarily many branches at each node. *) (* A purely functional tree with arbitrarily many branches at each node. *)
type 'a tree = type 'a tree =
| Leaf | 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

View File

@ -1,5 +0,0 @@
let id (x : 'a) = x
let ( >> ) f g x = g (f x)
let ( << ) g f x = g (f x)

View File

@ -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

View File

@ -1,5 +1,5 @@
open FromStdlib open FromStdlib
open Types open Exposed
let empty : 'a list = [] let empty : 'a list = []

View File

@ -1,4 +1,4 @@
open Types open Exposed
(** The empty list *) (** The empty list *)
val empty : 'a list val empty : 'a list

View File

@ -1,3 +1,3 @@
open FromStdlib open Types open FromStdlib open Exposed
let _ = printf "Hello, World\n" let _ = printf "Hello, World\n"

View File

@ -4,14 +4,13 @@ build:
ocamlopt -i fromStdlib.ml > fromStdlib.mli ocamlopt -i fromStdlib.ml > fromStdlib.mli
ocamlopt -S -O3 -c fromStdlib.mli fromStdlib.ml ocamlopt -S -O3 -c fromStdlib.mli fromStdlib.ml
# types for other modules separated to easily expose without module reference in projects # exposed types and functions, that can be opened module wide
ocamlopt -S -O3 -nopervasives -c types.mli types.ml ocamlopt -S -O3 -nopervasives -c exposed.mli exposed.ml
# the following files make up the core custom standard library code # 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 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 float.mli float.ml ocamlopt -S -O3 -nopervasives -c option.mli option.ml
ocamlopt -S -O3 -nopervasives -c functions.mli functions.ml
ocamlopt -S -O3 -nopervasives -c stack.mli stack.ml ocamlopt -S -O3 -nopervasives -c stack.mli stack.ml
ocamlopt -S -O3 -nopervasives -c list.mli list.ml ocamlopt -S -O3 -nopervasives -c list.mli list.ml
ocamlopt -S -O3 -nopervasives -c map.mli map.ml ocamlopt -S -O3 -nopervasives -c map.mli map.ml
@ -24,7 +23,7 @@ build:
ocamlopt -S -O3 -nopervasives -c main.mli main.ml 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 # 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 removes all except source files. autogenerated mli files are also removed.
clean: clean:

23
lib/option.ml Normal file
View 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
View 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

View File

@ -1,5 +1,5 @@
open List open List
open Types open Exposed
let enqueue (a : 'a) (qu : 'a queue) : 'a queue = let enqueue (a : 'a) (qu : 'a queue) : 'a queue =
{ qu with front = a :: qu.back } { qu with front = a :: qu.back }

View File

@ -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). *) (** Adds an element to the back of the queue, returning the new queue. Runs in O(1). *)
val enqueue : 'a -> 'a queue -> 'a queue val enqueue : 'a -> 'a queue -> 'a queue

View File

@ -1,5 +1,5 @@
open FromStdlib open FromStdlib
open Types open Exposed
let pop (st : 'a stack) : 'a option * 'a stack = let pop (st : 'a stack) : 'a option * 'a stack =
match st with match st with

View File

@ -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). *) (** 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 val pop : 'a stack -> 'a option * 'a stack

View File

@ -1,5 +1,5 @@
open FromStdlib open FromStdlib
open Types open Exposed
let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree = let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree =
Branch (topBranch, tr1 :: tr2 :: []) Branch (topBranch, tr1 :: tr2 :: [])

View File

@ -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). *) (* 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 val combine : 'a tree -> 'a tree -> 'a -> 'a tree