effect

Package Version Hex Docs

gleam add effect@1.2.0

This library includes an implementation of Effect that handles results differently. See effect_result for details.

import effect

pub fn main() {
  let google: Effect(Result(String, Error)) = {
    use uri <- effect.try_replace_error(
      uri.parse("https://www.google.com"),
      UriParse,
    )
    use req <- effect.try_replace_error(request.from_uri(uri), UriParse)
    use res <- effect.try_await_map_error(fetch.send(req), Fetch)
    use text <- effect.try_await_map_error(fetch.read_text_body(res), Fetch)

    text.body |> Ok |> effect.dispatch
  }

  use res <- effect.perform(google)
  case res {
    Ok(body) -> io.println(body)
    Error(e) -> e |> string.inspect |> io.println_error
  }
}

Further documentation can be found at https://hexdocs.pm/effect.

Development

gleam run   # Run the project
gleam test  # Run the tests

Effect With Result

A lightweight library for modeling asynchronous effects and error handling in Gleam, inspired by effect-ts.

This library allows you to treat asynchronous operations and potential failures as first-class effectful computations, which can be composed in a purely functional style before finally being executed.

The motivation was to create an API for dealing with promises from gleam_promises without having to “color” functions.

Table of Contents

  1. Overview
  2. Usage

Overview

Usage

Below are common usage scenarios demonstrating how to create and compose effects, handle failures, work with promises, and finally execute the effect.

Basic Construction

import effect/effect_result as effect
import gleam/io

pub fn main() {
  // Construct an effect from a plain Result:
  let eff_from_result = effect.from_result(Ok("Hello"))

  // Succeed / Fail shortcuts:
  let eff_ok = effect.succeed(42)
  let eff_err = effect.fail("Oops!")

  // Perform them:
  effect.perform(eff_from_result, fn(res) {
    case res {
      Ok(str) -> io.println("Got: " <> str)
      Error(err) -> io.println("Error: " <> err)
    }
  })
}

Chaining Effects

Use try to sequence operations, only continuing on success, or short-circuiting on error:

import effect/effect_result as effect
import gleam/io

type TooSmallError {
  TooSmallError
}

pub fn main() {
  let computation =
    effect.succeed(10)
    // successful effect
    |> effect.try(fn(x: Int) {
      // 
      case x < 5 {
        True -> effect.fail(TooSmallError)
        False -> effect.succeed(x * 2)
      }
    })
    |> effect.map(fn(double: Int) { double + 1 })

  effect.perform(computation, fn(res: Result(Int, TooSmallError)) {
    case res {
      // prints 21 if x=10
      Ok(n) -> io.println("Final result: " <> int.to_string(n))
      // TooSmallError
      Error(_err) -> io.println("Error: " <> "Too small!")
    }
  })
}

Handling Promises

You can convert a promise.Promise(Result(a, e)) into an Effect(a, e) using from_promise. Then compose it with try_await to sequence a follow-up effect:

type FetchError {
  FetchError(String)
}


fn fetch_data() -> promise.Promise(Result(String, FetchError)) {
  // For example, some JavaScript network call
  promise.new(fn(resolve) {
    // resolve(Ok("Server data"))
    resolve(Error(FetchError("Network error")))
  })
}

fn main() {
  // `try_await` waits for Ok(a), then calls `f(a) -> Effect(b, e)`
  let eff =
    effect.try_await(fetch_data(), fn(data) {
      // do something with the data
      effect.from_result(Ok("Got data: " <> data))
    })

  effect.perform(eff, fn(res) {
    case res {
      // Server Data
      Ok(msg) -> io.println(msg)
      // Network Error
      Error(FetchError(msg)) -> io.println("Failed: " <> msg)
    }
  })
}

If you need to map the error type before continuing, use try_await_map_error.

Search Document