Coder Social home page Coder Social logo

reazen / relude Goto Github PK

View Code? Open in Web Editor NEW
268.0 9.0 41.0 8.72 MB

FP-inspired prelude/standard library for ReasonML projects

Home Page: https://reazen.github.io/relude

License: MIT License

Nix 0.02% Reason 99.98%
prelude bucklescript reasonml stdlib-replacement stdlib utility

relude's Introduction

Relude

GitHub CI npm Coveralls

Relude is a ReasonML/OCaml standard library replacement ("prelude") written in ReasonML, targeting compilation to JavaScript via the Melange compiler.

Please visit the documentation site for more information!

relude's People

Contributors

andywhite37 avatar anmonteiro avatar arecvlohe avatar austindd avatar bobzhang avatar cakekindel avatar dependabot[bot] avatar endlo avatar jdeisenberg avatar jihchi avatar johnhaley81 avatar joprice avatar liboshen avatar mattphillips avatar maxkorp avatar mlms13 avatar rolandpeelen avatar utkarshkukreti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

relude's Issues

Things that don't belong here

...or maybe they do, idk. But these are some utility libraries that I'd like to have a "de facto" standard for.

  • URL
    • relude-url
    • fully typed
    • easily append path, query strings, etc
    • convert to/from ReasonReact Router URL type
    • might depend on relude-parser for parsing URLs
  • Color
    • relude-color
    • Output as hex, rgb(a), hsl(a) strings
    • Blend, darken, lighten, and other transformations
    • Anything else in Elm.Color or any other common Color libraries
  • Simple monadic parser library
    • relude-parser
  • AJAX/Fetch utilities
    • Need to get IO to a stable state first
    • Bindings to browser-based fetch or bs-fetch
  • Dates
    • Represent local date (and time) with no timezone
    • Have a type that turns a local date into an instant (precise moment in time)
    • Minimal utilities for converting to/from Js.Date.t
    • Formatters that don't depend on magic strings
  • Lenses
  • Streams
    • Check out Scala FS2 for ideas
    • Also check Haskell, Purescript and Elm ecosystems (or any other things) for ideas
  • RationalJS Future interop library (with IO)
    • Not sure how critical this is, but might be helpful to somebody?
  • Data structures
    • Stack
    • Queue
    • HashMap/HashSet/hashing library
    • Heap
    • etc.

Structure

I forget how close we actually got to this, but the following code kind of outlines how I'd like typeclasses to work:

Foldable.max((module List.Foldable), (module Int.Ord), [ 3, 1, 7, 2 ]);
Foldable.maxBy((module Array.Foldable), Int.compare, [| 4, 6, 1 |]);

List.max((module Float.Ord), [ 3.1, 3.6, 4.2 ]);
List.maxBy(Float.compare, [1.2, 1.1, 1.0]);

List.Int.max([1, 2, 3]);

With that same pattern extended to Functor/Applicative/Monad/Traversable and all of the implementation types.

To summarize: structure things as if modular implicits were a thing, then build specialized modules since the implicit resolution isn't real yet, and possibly more specific sub-modules so you don't have to pass modules anywhere if you don't want to.

Not saying this is a radical departure from what we've already been doing... just wanted to formalize it a bit more.

Use function first for `flatMap` and add `bind` as value first.

There are some annoyances in how flatMap is currently defined as "value first."

I talked to @mlms13, and we decided that we could have a bind function as (t('a), 'a => t('b)) => t('b)) with the usual infix of >>=, and then settled on the convention that flatMap is flip(bind): ('a => t('b), t('a)) => t('b).

This would make it easier to consistently use |> for chaining with the named functions, and not have to resort to -> for flatMap.

Tuple utilities

It would be handy to add some helpers for dealing with tuples.

One thing I can think of off the top of my head is a set of applicative map2/map3/etc. functions for a tuple. It will unfortunately have to use a module functor to get the applicative behavior, but we can add some specializations e.g. (pseudocode):

let fa: option(int) = Some(1);
let fb: option(int) = Some(2);
let fc: option(int) = (fa, fb) |> Relude.Tuple.Option.map2((a, b) => a + b));

Or maybe it should be Relude.Option.Tuple?

This would be equivalent to Relude.Option.map2((a, b) => a + b, fa, fb) but with better chainability and readability.

Decimal type

It would be handy to have a arbitrary precision numeric type, like BigDecimal from Java/Scala.

We could also consider adding a Numeric typeclass that could be brought into scope for a specific numeric type to give access to basic infix operators.

Change names of all functions currently named `get` or `set`

Both refmt and the OCaml compiler seem to do strange things with String.get, even if you've shadowed the Pervasives String module locally. This could also happen for Array (and BigArray and other specific modules, I think).

The following function names are at risk of being handled incorrectly depending on the module name:

  • get
  • unsafe_get
  • set
  • unsafe_set

We should purge all of these names from Relude to avoid any potential issues.

Option

I'm going to run through all of the option utilities I've used and written recently and make a list of all of the helper functions here:

  • (b, a => b, f(a)) => b

    • fold in Scala
    • maybe in PureScript and bs-abstract
    • mapWithDefault in Belt
    • unwrap in Elm (Maybe.Extra)
    • map_default in Batteries
    • cata in Thx
  • (a, f(a)) => a

    • getOrElse in Scala and Thx
    • fromMaybe in PureScript
    • withDefault in Elm
    • getWithDefault in Belt
    • default in Batteries
    • infix (|?) in bs-abstract
  • ((b, a) => b, b, f(a)) => b (traditional fold left, is this even useful?)

    • foldLeft in Scala (not sure if it's present for Option)
    • foldl in PureScript (and Elm, but not present for Maybe)
    • fold_left in bs-abstract
    • reduce in Belt and fantasy-land, but not present for Option
  • (f(a), f(a)) => f(a)

    • alt in PureScript, bs-abstract, fantasy-land
    • or in Elm (Maybe.Extra)
    • flipped as orElse in Elm (Maybe.Extra)
  • (a => b, f(a)) => f(b)

    • map pretty much everywhere
  • (f(a => b), f(a)) => f(b)

    • ap in PureScript and fantasy-land
    • apply in bs-abstract
    • andMap in Elm (Maybe.Extra)
  • a => f(a)

    • pure in PureScript and bs-abstract
    • of in fantasy-land
    • doesn't appear to exist in Belt or Elm
  • (a => f(b), f(a)) => f(b)

    • flatMap in Scala and Belt
    • flat_map (flipped) in bs-abstract
    • bind (flipped) in PureScript and Batteries
    • andThen in Elm
    • chain in fantasy-land
  • ((a, b) => c, f(a), f(b)) => f(c) and friends

    • map2 in Elm
    • lift2 in PureScript and bs-abstract
    • liftA2 in Haskell
    • ap2 in Thx
  • toList

  • toArray

  • lazy versions of maybe and default

  • isSome

  • isNone

  • filter

  • join

  • infix functions

    • alt
    • default (flipped)
    • map
    • ap
    • flatMap (flipped)

Change Option.fold/foldStrict to make the strict version the default

I originally wanted the default Option.fold function to be lazy for the None case, to avoid constructing values that never get used. I added Option.foldStrict to be the non-lazy, but more commonly-wanted version. However, in practice, people typically want to just use the signature of foldStrict, with the option of having the lazy version available for certain cases.

This is in-line with how purescript handles this for Maybe: https://github.com/purescript/purescript-maybe/blob/master/src/Data/Maybe.purs#L209-L232

I suggest we rename the current Option fold to foldLazy and rename foldStrict to fold to make the non-lazy version the default, with the option of a lazy one that can be used if desired. I'm open to suggestions on better names for foldLazy.

I know @mlms13 is on board with this!

Change to namespace: false config and rename files to `Relude_MyModule.re` convention

namespace: true is convenient in terms of having easy-to-reference modules in the project, but I'm running into more and more conflicts and name resolution issues with ocaml std lib modules like String and List, which have no base Stdlib base module to use to resolve conflicts. The Stdlib module was added in ocaml 4.07, but bucklescript is currently based on ocaml 4.02.3.

I'm proposing that we set namespace: false in bsconfig, rename our modules from List.re to Relude_List.re and create the base module Relude.re which aliases all of our longer names like module List = Relude_List

Thoughts?

Future type

It would be nice to have our own Future type, so we have full control over the implementation, and can make it consistent with our other abstractions.

Remove redoc and redoc-generated documentation

I'm having trouble with nixos and redoc stuff, and since we are planning to switch from redoc to something else anyway, I'm going to just remove redoc from the package.json, the docs folder, the current gh-pages site, etc.

(If we were planning on staying on redoc, I would have tried harder to fix my local setup 😃)

NonEmpty* types

Consider bringing mlms13/bs-nonempty stuff into this project?

Or we could alias it, or just let it live outside on its own.

Result.fold and Option.fold disagree about param order

  • Result.fold expects the Ok handler to come first and the Error handler to come second.
  • Option.fold expects the None handler to come first and the Some handler to come second

I think i like providing the "bad case" handler first, but I don't have a super strong opinion. Either way, this should probably change to be more consistent.

Move scanl/scanr to Foldable/Traversable helper module

If you believe Purescript, scanl and scanr should come for free with anything that is Traversable, but I looked at their implementations and I'd be lying if I said it was immediately clear to me how we get there...

Add Foldable.surround

List.surround((module String.Semigroup), "-", []); // "-"
List.surround((module String.Semigroup), "a", ["h"]); // "aha"
List.surround((module String.Semigroup), "*", ["1", "2", "3"]); // "*1*2*3*"

This is a lot like Foldable.intercalate except the "separator" is also added to the beginning and end. This also means that the inner type only needs to implement Semigroup (not Monoid) because the separator is used as the initial value.

Monad transformers

Once we get a little farther along with our own Future type, it would be interesting to try some monad transformers, like ResultT to handle things like Future.t(Result.t(ok, err)), which seems to be a common pattern.

  • ContT
  • ResultT (aka ExceptT)
  • ListT (not sure if we want this one)
  • OptionT (aka MaybeT)
  • ReaderT
  • WriterT
  • StateT
  • RWST (reader/writer/state)

Fix up string replacement

  1. Rename String.replace to String.replaceFirst
  2. Add String.replaceAll, possibly using Elude as a model

RemoteData-like type

Does this belong in this project, or is it best to leave it in a separate lib?

The benefit of having something like that in this project is consistency with our other conventions.

File organization for typeclass-based utilities, specialized functions, etc

  1. Create a collection of "things that come for free if you can implement typeclass x"
  • e.g. countBy is free if your thing is Foldable
  • e.g. max is free if your thing is Foldable and the inner thing has Ordering
  1. Solve the ListF thing
  • e.g. move sumInt into List.Int.sum
  • figure out how to alias such things in the top-level module to make a nice experience for users of Relude

Restructure Array to match List

This should be pretty quick, since Array is already a member of Foldable, and a whole bunch of List functions were rewritten to come for free from Foldable.

Documentation HTML files hang in Firefox

  1. Forked reazen/relude
  2. Cloned the fork
  3. Went to Mozilla Firefox 67.0b4 (64-bit) on Fedora 29
  4. Entered URL: file:///home/david/reasonlab/relude/docs/api/Relude.html
  5. Page does not plot; displays “loading circle” animation. Web console shows no error messages.

Works correctly on Chrome Version 73.0.3683.75 (Official Build) (64-bit)

Release script

Talking to @mlms13 - I think we decided on a basic release procedure. We're going to just do releases by hand for now, until we have a good procedure for doing automatic CI releases.

> ./release <bump>

where <bump> is major|minor|patch

This would perform the following operations:

...developer manually updates CHANGELOG.md to add summary of changes
...or maybe we can generate the changelog via PR metadata since the last tag?
...getting the new version # at this point might be tricky

# Update the version number in package.json and create the git tag
npm version $bump
npm publish

# Push the tags to the root repo
# Note: try to use an explicit reference to the root repo, rather than `upstream`
git push upstream --tags

# Create the github release
...Use github API to create a release for this new version
or
echo "Create github release now"

Add submodules to Foldable helpers

If you look at the Int submodule in ListF, you'll see that most of that duplicates the String submodule in that same file, and when we add Float, it will be even more duplication.

I think we can probably cut down on some of this by getting rid of the first-class-module functions in the Foldables helper, and instead, we can add Eq and Ord submodules there. Then, ListF.Int can look something like:

module Int = { 
  module FoldOrdFns = Foldable.Eq(List.Foldable, Int.Ord);
  module FoldMonoidFns = Foldable.Eq(List.Foldable, Int.Additive.Monoid);

  include FoldOrdFns;
  let sum = FoldMonoidFns.fold;
};

Theoretically, we can get all of the Eq functions for free from Ord, and we can alias functions as needed. If this works, it should lead to a lot less duplication across ListF.String, ListF.Int, ListF.Float, etc.

AsyncData type parameters

@fponticelli has proposed that Refreshing should have it's own type parameter - but there are some other options to consider too:

type t('a, 'r, 'e) =
| Idle
| Loading
| Loaded('a)
| Refreshing('r)
| Failed('e)
;

The downside of this is that it becomes more of a bifunctor, so the basic applicative/functor/monad/etc. will only operate on the Loaded value, rather than on Refreshing too.

Another option would be to give Loading a value of either type 'a or another type:

| Idle
| Loading('a)
| Loaded('a)
| Failed('e)

You can choose your 'a to represent whatever you want, including unit for the first load, then something else when loaded/refreshing. the only semantic is whether the whole thing is Loading or Loaded.

Or even take it one more step:

| Idle('a)
| Loading('a)
| Loaded('a)
| Failed('e)

Or this:

| Idle('i)
| Loading('l)
| Loaded('a)
| Failed('e)

Thoughts?

Add min/max to Foldable.Ord

When the outer thing is Foldable and the inner thing has Ordering, min and max should come for free. This is mostly useful for Int and Float.

Change take/drop to avoid `option`

Currently the signature of take is (int, list('a)) => option(list('a)), probably because that's what Belt does by default.

But "monoid of a monoid" is always awkward (is Some([]) different from None? are we implying some kind of weird semantics here?), and in practice, I always use takeUpTo and can't say I've ever touched take.

I propose we change take to (int, list('a)) => list('a) (which is consistent with PureScript). We can keep the current take behavior with a different name. @andywhite37 suggested takeExactly, which I think is a pretty 👍 name.

I'm in the middle of a small refactor where I'm trying not to make interface-breaking changes, so I'm going to hold off on this for a bit. Consider it open-for-discussion-but-likely-to-change.

Rename removeF to removeBy

...and all of the other ...F suffixed functions. This seems to be consistent with PureScript's maximumBy where you pass in an a -> a -> Ordering instead of a member of Ord.

Initially I was thinking that the function-based (as opposed to module-based) versions should be the "default" (e.g. eq would be the function-based version instead of using an Eq module), but prioritizing modules feels "more correct", and I don't mind the ...By suffix quite as much.

Relude.Option.Infix.(>>=) is flipped

Relude.Option.Infix.(>>=) appears to have its arguments in the wrong order, at least compared to what I'm used to.

$ ghci
Prelude> Just 5 >>= \n -> Just $ n + 1
Just 6
> Relude.Option.Infix.((n => Some(n + 1)) >>= Some(5));
Some(6)

Versioning strategy

We should discuss a versioning/release strategy.

Although it's not required, it's probably smart to push releases to npm, https://redex.github.io, or whatever other places people push ReasonML projects.

Idea for a basic process:

  1. Follow semantic versioning principles and version naming conventions
  2. Merge PRs/changes/etc. directly into master
  3. When we have a set of features/fixes we want to release:
    1. make sure we have a good CI build (see #39)
    2. determine whether it's a major/minor/patch update
    3. Use the following commands to bump the version and publish the project:
> npm version major|minor|patch
> npm publish
> submit a PR to redex (details TBD)

@mlms13 - thoughts?

Attempt to use a normal (non-polymorphic) variant for Ordering

bs-abstract uses a polymorphic variant for ordering: https://github.com/Risto-Stevcev/bs-abstract/blob/master/src/interfaces/Interface.re#L130

It would be useful to understand why a polymorphic variant was used, rather than a non-polymorphic variant like:

type ordering = | EQ | LT | GT

Since ordering is not likely to be extended, I'm not sure what the reason was for using polymorphic.

We could deviate from bs-abstract if we wanted to, but it would be nice to keep a compatibility layer if we do.

Surprising result from Relude.Array.drop and Relude.Array.take

Is this the expected behavior, especially from drop?

take(-1, [|100, 101, 102|]) == Some([| |]);
take(-2, [|100, 101, 102|]) == Some([| |]);
take(-3, [|100, 101, 102|]) == Some([| |]);
take(-4, [|100, 101, 102|]) == Some([| |]);

but...

drop(-1, [|100, 101, 102|]) == Some([|102|]);
drop(-2, [|100, 101, 102|]) == Some([|101, 102|]);
drop(-3, [|100, 101, 102|]) == Some([|100, 101, 102|]);
drop(-4, [|100, 101, 102|]) == Some([|100, 101, 102|]);

I would expect all of the above to return None.

After looking at the code: it appears that take() and drop() call JavaScript’s Array.Prototype.slice(), which gives these exact weird-seeming results.

AsyncData helpers

AsyncData status as bool:

  • isInit
  • isBusy -- true for Loading or Reloading
  • isLoading -- true for Loading, false for Reloading
  • isReloading
  • isComplete
  • isSuccess, isFailure (or isCompleteOk, isCompleteError?) for AsyncResult

AsyncData get option:

  • getComplete
  • getOk, getError for AsyncResult

AsyncData/Result helpers

Add some folding functions to help with the verbose pattern matches with AsyncResult.

Also add some helper functions to transition an AsyncData/Result from one state to another, carrying state over as appropriate (e.g. toLoading, etc.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.