This repository has been archived by the owner on Jul 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Foundation to provide cross-compatibility with native
- Loading branch information
1 parent
951e35e
commit cd485a1
Showing
4,201 changed files
with
9,910 additions
and
290 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,5 @@ lib/bs | |
.bsb.lock | ||
lib | ||
node_modules | ||
_build/ | ||
_opam/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
version=0.13.0 | ||
margin=100 | ||
break-cases=fit-or-vertical | ||
break-collection-expressions=fit-or-vertical | ||
break-fun-decl=fit-or-vertical | ||
break-infix=fit-or-vertical | ||
break-struct=force | ||
break-sequences=true | ||
field-space=loose | ||
if-then-else=keyword-first | ||
indicate-multiline-delimiters=no | ||
infix-precedence=parens | ||
parens-tuple=multi-line-only | ||
sequence-style=terminator | ||
type-decl=sparse | ||
wrap-comments=false | ||
wrap-fun-args=false | ||
space-around-arrays=false | ||
space-around-lists=false | ||
space-around-variants=false | ||
space-around-records=true | ||
doc-comments=before |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
## Potentially breaking changes | ||
|
||
- `Float.Show` now uses `string_of_float` for cross-compatibility. This might be undesirable | ||
depending on your use cases, in which case `JsFloat.Show` is provided which uses | ||
`Js.Float.toString`. See [bucklescript#3412][1]. | ||
|
||
- The `JsArray` module is provided for users that want to have the transpiled bucklescript code use | ||
the builtin `Array.prototype` api instead of the version used for Ocaml compatibility. Using this | ||
on the frontend would mean potentially smaller bundle sizes (implementations are built into JS), | ||
and possible performance improvements since the javascript engines know explicitly what the code | ||
is trying to do. | ||
|
||
- `Result.Unsafe` functions now raise an Ocaml `Invalid_argument` error instead of a JS type error | ||
on failure. No js compat is currently provided since these are unsafe functions to begin with and | ||
shouldn't be used. | ||
|
||
|
||
## Notes | ||
|
||
These are updates that shouldn't cause any breaking changes but are documentated here | ||
|
||
- `Int.EuclideanRing` also uses the builtin `min` function now. This functions behaves identically | ||
to the js counterpart `Js.Math.min`, it's just noted here for reference. | ||
|
||
- `Option.getWithDefault` copies the same implementation from bucklescript's stdlib so that it works | ||
on native as well. | ||
|
||
|
||
[1]: https://github.com/BuckleScript/bucklescript/issues/3412 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
all: build | ||
|
||
.PHONY: clean-bs | ||
clean-bs: | ||
bsb -clean-world | ||
|
||
.PHONY: clean-native | ||
clean-native: | ||
dune clean | ||
|
||
.PHONY: clean-docs | ||
clean-docs: | ||
rm -rf docs/** | ||
|
||
.PHONY: clean | ||
clean: clean-bs clean-native clean-docs | ||
|
||
.PHONY: build-bs | ||
build-bs: | ||
bsb -make-world | ||
|
||
.PHONY: build-native | ||
build-native: | ||
dune build @all | ||
|
||
.PHONY: build | ||
build: build-bs build-native | ||
|
||
.PHONY: fmt | ||
fmt: | ||
dune build @fmt --auto-promote | ||
|
||
.PHONY: docs | ||
docs: clean-docs | ||
dune build @doc | ||
|
||
.PHONY: copy-docs | ||
copy-docs: docs | ||
cp -r _build/default/_doc/_html/** docs/ | ||
|
||
.PHONY: open-docs | ||
open-docs: copy-docs | ||
xdg-open docs/index.html | ||
|
||
.PHONY: test-bs | ||
test-bs: | ||
yarn test | ||
|
||
.PHONY: test-native | ||
test-native: | ||
dune runtest --no-buffer | ||
|
||
.PHONY: test | ||
test: test-bs test-native | ||
|
||
.PHONY: watch-native | ||
watch: | ||
dune build @all -w | ||
|
||
.PHONY: watch-bs | ||
watch-bs: | ||
bsb -make-world -w | ||
|
||
.PHONY: watch-test-bs | ||
watch-test-bs: | ||
yarn run watch-test | ||
|
||
.PHONY: watch-test-native | ||
watch-test: | ||
dune runtest --no-buffer -w | ||
|
||
.PHONY: utop | ||
utop: | ||
dune utop . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,140 +1,140 @@ | ||
# bs-abstract | ||
# ocaml-abstract | ||
|
||
Bucklescript interfaces and implementations for category theory and abstract algebra | ||
A ReasonML/Ocaml library for category theory and abstract algebra. | ||
|
||
<img src="https://raw.githubusercontent.com/Risto-Stevcev/bs-abstract/master/cantellated_tesseract.png" height="100" width="100"/> | ||
|
||
|
||
## Documentation | ||
|
||
See [documentation][1] | ||
|
||
|
||
## Installation | ||
|
||
Install the project: | ||
|
||
`npm install bs-abstract --save` | ||
`npm install ocaml-abstract --save` | ||
|
||
And add the dependency to your bs-dependencies in `bsconfig.json`: | ||
|
||
```json | ||
"bs-dependencies": [ | ||
"bs-abstract" | ||
"ocaml-abstract" | ||
] | ||
``` | ||
|
||
The project will be available under the `BsAbstract` namespace | ||
|
||
## Project Layout | ||
|
||
This is the current layout of the project. It's subject to change: | ||
|
||
- [src/interfaces/Interface.re][1] - Contains the category theory and abstract algebra interfaces | ||
- [src/interfaces/Verify.re][2] - Contains property based tests to verify that implementations are lawful | ||
- [src/interfaces/Infix.re][3] - Contains functors to generate infix operators for the interfaces. Modules implementing interfaces contain an already instantiated Infix module for convenience where appropriate | ||
- [src/utilities/Default.re][4] - Contains default implementations for interface functions | ||
- [src/utilities/Functors.re][5] - Contains already instantiated functors for common data combinations for convenience | ||
- [src/functions/Functions.re][6] - Contains generic functions that are built on top the abstract interfaces | ||
- [src/implementations/][7] - Contains implementations for common bucklescript types | ||
## Examples | ||
|
||
The rest of the files under `src` are implementations based on data type (ie: `String.re` for strings). These files and their corresponding unit tests in the `test` folder will give you an idea on how to use and implement the interfaces for your own data structures. | ||
|
||
## Suggested Usage | ||
```ocaml | ||
# #require "ocaml_abstract";; | ||
# open Ocaml_abstract;; | ||
# module T = Functors.ListF.Option.Traversable;; | ||
module T = Ocaml_abstract.Functors.ListF.Option.Traversable | ||
- The suggested way to combine monadic code is to use kliesli composition instead of `flat_map`. For example, given a | ||
type that's a monad, a very common pattern is to get the inner value and pass it in as an argument to a | ||
subsequent function, like so: | ||
# T.sequence [Some "foo"; Some "bar"];; | ||
- : string list option = Some ["foo"; "bar"] | ||
```reason | ||
module I = Functions.Infix.Monad(BsEffects.Effect.Monad); | ||
# Functors.ListF.Int.Show.show [1; 1; 2; 3; 5; 8];; | ||
- : string = "[1, 1, 2, 3, 5, 8]" | ||
``` | ||
|
||
let exclaim_file = path => BsEffects.Effect.Infix.({ | ||
read_file(path) >>= contents => { | ||
write_file(path, contents ++ "!") | ||
} | ||
}); | ||
``` | ||
## Suggested Usage | ||
|
||
Which looks like this using do notation (in haskell): | ||
### Use kliesli composition | ||
|
||
```haskell | ||
contents <- read_file "foo" | ||
_ <- write_file "foo" (contents ++ "!") | ||
``` | ||
The monadic equivalent of the `|>` operator is `flat_map` (`>>=`). Both are very useful for writing | ||
code that's easy to reason about as data all flows in one direction. | ||
|
||
This can be written with kliesli composition like this: | ||
However, function composition (`>.`) and kliesli composition for monads (`>=>`) are often underused | ||
in code. Composing functions and monads monoidally like this in a concatenative style can make a | ||
codebase easier to read and can also prevent devs on the team from duplicating code. | ||
|
||
```reason | ||
module Effect_Infix = Functions.Infix.Monad(BsEffects.Effect.Monad); | ||
let ((>=>), (>.)) = (Effect_Infix.(>=>), Function.Infix.(>.)); | ||
Consider a typical use case. Often in a codebase you have something that fetches a value that may or | ||
may not exist (`'a option`). Splitting out this code into smaller functions and combining (and | ||
reusing) them with composition leads a lean code style: | ||
|
||
let exclaim = Function.flip((++))("!"); | ||
let exclaim_file = path => Function.const(read_file(path)) >=> (exclaim >. write_file(path)); | ||
``` | ||
```ocaml | ||
# let ((>=>)) = Option.Infix.((>=>));; | ||
val ( >=> ) : ('a -> 'b option) -> ('b -> 'c option) -> 'a -> 'c option = | ||
<fun> | ||
Building up functions using function and kliesli composition is a good litmus test that your program | ||
is built up from generic, pure abstractions. Which means that the code is easy to abstract to make it reusable in many | ||
other contexts, and abstractions are easy to decompose when requirements change. | ||
# type form = { name: string; address: string option };; | ||
type form = { name : string; address : string option; } | ||
# let get_form () = | ||
(* Assume some side effect got the form here *) | ||
Some { name = "Foo"; address = Some "123 Bar St." };; | ||
val get_form : unit -> form option = <fun> | ||
- For interfaces based on functors, Use already instantiated functors if available to avoid the extra boilerplate, ie: | ||
```reason | ||
ArrayF.Int.Additive.Fold_Map.fold_map | ||
``` | ||
# let get_address form = form.address;; | ||
val get_address : form -> string option = <fun> | ||
- Don't overuse infix operators. If the code is combinatorial it can make it more readable, but a lot of times prefix operators are simpler and easier to read | ||
- If you do use infix operators, prefer local opens over global opens, and prefer explicit unpacking over local opens, ie: | ||
# let get_form_address = get_form >=> get_address;; | ||
val get_form_address : unit -> string option = <fun> | ||
```reason | ||
let ((<.), (>.)) = Function.Infix.((<.), (>.)) | ||
``` | ||
# get_form_address ();; | ||
- : string option = Some "123 Bar St." | ||
``` | ||
|
||
- Abbreviated modules can make code terser and easier to read in some situations (ie: `A.map`), especially in situations where infix operators can't be used because they would introduce ambiguity, like for example when two different monoids are used in the same function. | ||
### Instantiated Functors | ||
|
||
For interfaces based on functors, use already instantiated functors if available to avoid the extra | ||
boilerplate: | ||
|
||
Example code: | ||
```reason | ||
module T = ListF.Option.Traversable; | ||
assert(T.sequence([Some("foo"), Some("bar")]) == Some(["foo", "bar"])); | ||
Js.log(ListF.Int.Show.show([1,1,2,3,5,8])); | ||
```ocaml | ||
# Functors.ArrayF.Int.Additive.Fold_Map.fold_map | ||
- : ('a -> int) -> 'a array -> int = <fun> | ||
``` | ||
|
||
See the unit tests for many more examples | ||
### Don't Overuse Infix | ||
|
||
## Side effects / IO | ||
Don't overuse infix operators. If the code is combinatorial it can make it more readable, but in a | ||
lot of cases the prefix operators are simpler and easier to read. If you do use infix operators, | ||
prefer local opens over global opens to avoid polluting the toplevel: | ||
|
||
See the [bs-effects][8] package for sync and async implementations of the "IO monad", and | ||
the [bs-free][9] package for free monads and other free structures. | ||
|
||
## Use with ppx_let | ||
```ocaml | ||
# let trim_all strings = | ||
let open List.Infix in | ||
StringLabels.trim <$> strings;; | ||
val trim_all : string list -> string list = <fun> | ||
You can integrate monads with [ppx_let](https://opam.ocaml.org/packages/ppx_let/), a ppx rewriter that provides | ||
"do notation" sugar for monads. The rewriter expects a `Let_syntax` module to be in scope, which you can construct | ||
using `PPX_Let.Make`, like so: | ||
# trim_all ["foo "; "bar"; " baz"];; | ||
- : string list = ["foo"; "bar"; "baz"] | ||
``` | ||
|
||
```ocaml | ||
module OptionLet = PPX_Let.Make(Option.Monad);; | ||
### Use Abbreviated Modules | ||
|
||
let add_optionals = fun x y -> | ||
let open OptionLet in | ||
let%bind x' = x in | ||
let%bind y' = y in | ||
Some (x' + y');; | ||
Abbreviated modules can make code both terser and easier to read in some situations, like for | ||
example where two different semigroups are used in the same function and infix operators can't be | ||
used: | ||
|
||
Js.log @@ add_optionals (Some 123) (Some 456);; (* Some 579 *) | ||
```ocaml | ||
# type game = { score: int; disqualified: bool };; | ||
type game = { score : int; disqualified : bool; } | ||
# let total_score a b = | ||
let module I = Int.Additive.Semigroup in | ||
let module B = Bool.Disjunctive.Semigroup in | ||
{ score = I.append a.score b.score; disqualified = B.append a.disqualified b.disqualified };; | ||
val total_score : game -> game -> game = <fun> | ||
# let result = | ||
let game_1 = { score = 4; disqualified = false } | ||
and game_2 = { score = 2; disqualified = true } | ||
in | ||
total_score game_1 game_2;; | ||
val result : game = {score = 6; disqualified = true} | ||
``` | ||
|
||
Currently as of this writing, there's no support for `let%bind` style syntax for ReasonML, but it | ||
should be available in one of the next releases | ||
|
||
|
||
## License | ||
|
||
Licensed under the BSD-3-Clause license. See `LICENSE` | ||
|
||
See [LICENSE][2] | ||
|
||
|
||
[1]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/interfaces/Interface.re | ||
[2]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/interfaces/Verify.re | ||
[3]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/interfaces/Infix.re | ||
[4]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/utilities/Default.re | ||
[5]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/utilities/Functors.re | ||
[6]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/functions/Functions.re | ||
[7]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/implementations | ||
[8]: https://github.com/Risto-Stevcev/bs-effects | ||
[9]: https://github.com/Risto-Stevcev/bs-free | ||
[1]: https://risto-stevcev.github.io/ocaml-abstract | ||
[2]: https://github.com/Risto-Stevcev/bs-abstract/blob/master/LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.