Relude Standard Library for ReasonML

repository·main·Indexed 42 days ago

https://github.com/reazen/relude

An alternative standard library (prelude) for ReasonML and OCaml, optimized for compilation to JavaScript via Melange. Relude provides typeclass-driven abstractions (such as Monad, Applicative, and Traversable), an IO monad for effect management, and monad transformers like OptionT, ResultT, and StateT. It follows significant-data-last semantics using the |> pipe operator and offers a robust set of utility functions for common data types.

Tokens
3.3K
Snippets
10
Records
26
Agent score
54%

What's inside Relude

  1. Overview of Relude

    main
    Relude is a standard library replacement (prelude) for ReasonML and OCaml. It is written in ReasonML and is designed to be compiled to JavaScript using the Melange compiler. It serves as a more robust and feature-complete alternative to the default standard libraries in these ecosystems.
  2. What is Relude?

    main

    Relude is a standard library for ReasonML inspired by functional programming preludes like Haskell, Purescript, Elm, and Scala (cats/scalaz). It is designed to provide powerful typeclass-style abstractions alongside practical utilities for everyday application-level work on JavaScript-based platforms (Browser and Node.js).

    Key characteristics:

    • Typeclass-driven: Uses abstractions like Applicative, Monad, and Traversable.
    • Opinionated: Follows JavaScript/Scala-style naming conventions (camelCase/TitleCase) rather than OCaml's snake_case.
    • Pipe Operator: Standardizes on the |> pipe operator.
    • Compatibility: Uses core typeclass interfaces from the mel-bastet library to ensure interoperability with other libraries using the same signatures.
  3. Why Relude lacks interface (.rei) files

    main

    Relude does not provide .rei files because it relies heavily on the ReasonML/OCaml module include capability.

    Many Relude typeclasses use extension modules that are include'd into your host module. This automatically adds utility functions, operators, and extension modules to your module based on the typeclass instances you create. Because these functions are added dynamically via inclusion, maintaining static .rei files that accurately reflect all automatically added functions while providing proper documentation and type annotations is currently unfeasible.

  4. Extend modules using Typeclass Extensions

    main

    Relude uses Typeclass Extensions to provide high-level functionality for free. When you implement a fundamental typeclass (like FUNCTOR, APPLY, APPLICATIVE, ALT, or MONAD), you can use Relude's Extension module functors to derive complex functions (like map2, traverse, or foldLeft) automatically.

    To use this pattern, you typically:

    1. Define your data type.
    2. Provide instances for the required typeclass signatures.
    3. Use the include mechanism to add the corresponding Extension modules directly to your module.
  5. Use Relude utility functions and modules

    main

    Relude provides a comprehensive set of low-level utility functions for common data types to replace the need for Belt or OCaml stdlib APIs. It also includes specialized modules and types for application development.

    Supported Data Types include:

    • list('a)
    • array('a)
    • option('a)
    • Belt.Result.t('a, 'e)

    Specialized Utility Modules/Types:

    • AsyncData and AsyncResult
    • Decimal
    • HList and HMap
    • ListZipper
    • Validation
  6. Use `*By` functions as an alternative to first-class modules

    main

    Because ReasonML/OCaml lacks implicit typeclass resolution (ad-hoc polymorphism), Relude often requires passing a first-class module to functions. To simplify this, Relude provides higher-order functions with a By suffix.

    Instead of manually creating a module that conforms to a typeclass signature (e.g., a SHOW module) and passing it to a function like show, you can use the showBy variant, which accepts a simple function (e.g., 'a => string) instead of a full module.

    Common patterns:

    • showBy instead of show (requires a function instead of a SHOW module).
    • eqBy instead of eq (requires a comparison function instead of an EQ module).
    • compareBy instead of compare.
  7. Use the pipe operator (|>) with significant-data-last semantics

    main

    Relude follows significant-data-last (or pipe last) semantics. Instead of using the -> operator (common in Belt), you should use the |> operator to thread data through functions.

    How it works: |> does not strictly thread a value into the last argument; it passes the value into the next unfilled argument of the function. This allows you to partially apply arguments and then feed the 'significant data' into the final argument.

    Benefits:

    • Works in native OCaml without requiring a PPX.
    • Enables natural partial application and composition.
    • Aligns with other functional programming ecosystems.
  8. Understand the relationship between Relude and Belt/OCaml stdlib

    main

    Relude is designed to augment existing types from Belt and the OCaml standard library. When using Relude, you will encounter three patterns:

    1. Aliasing: Relude may alias types from Belt or the OCaml stdlib to provide a consistent interface.
    2. Augmentation: Relude modules may provide additional utility functions for existing Belt or OCaml types.
    3. New Types: Relude defines its own unique types and modules (e.g., Relude.AsyncResult, Relude.ReaderT) that do not exist in the standard libraries.
  9. Extend functionality using Typeclasses

    main

    Relude leverages typeclass-style abstractions to provide extra functionality to your modules automatically. If you implement a standard typeclass module type (such as those from Bastet.Interface), you can include a wide variety of extension functions and infix operators for free.

    Instead of manually implementing functions like map2, flatten, length, or toArray for your custom types, you can implement the corresponding typeclass (e.g., MONAD), and Relude will provide these operations consistently and predictably.

  10. Perform a local dry run of the opam publishing process

    main

    To verify that the package is ready for publishing without actually submitting it, you can perform a local validation. This involves installing the opam-publish plugin, verifying metadata, checking for required fields, and ensuring the git state is valid.

    # Install opam-publish plugin
    opam install opam-publish
    
    # Test package metadata
    opam show --raw .
    
    # Validate required fields
    grep -q "^homepage:" relude.opam && 
    grep -q "^bug-reports:" relude.opam && 
    grep -q "^synopsis:" relude.opam && 
    echo "✅ Required fields present"
    
    # Check git state
    git rev-parse --verify HEAD
  11. Locate utility functions in Relude

    main

    Relude organizes utility functions within top-level modules corresponding to the data type they operate on.

    • For a specific type: Look in Relude.<Type> (e.g., Relude.Option for option('a) or Relude.String for string).
    • For specialized type combinations: Look in nested modules (e.g., Relude.List.String for functions operating on list(string)).
  12. Publish Relude manually to Opam

    main

    If automated publishing is unavailable, you can publish Relude to the opam repository manually. You must first clone the repository and checkout the specific release tag corresponding to the version you wish to publish.

    # Clone the repository and checkout the release tag
    git clone https://github.com/reazen/relude.git
    cd relude
    git checkout v<version>
    
    # Publish to opam
    opam publish --tag=v<version> .