{"id":37160669,"url":"https://github.com/midas-framework/midas","last_synced_at":"2026-01-14T19:06:09.962Z","repository":{"id":45824596,"uuid":"257535412","full_name":"midas-framework/midas","owner":"midas-framework","description":"A framework for Gleam, Midas makes shiny things.","archived":false,"fork":false,"pushed_at":"2025-07-02T10:40:24.000Z","size":2394,"stargazers_count":196,"open_issues_count":0,"forks_count":5,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-11-22T18:21:51.843Z","etag":null,"topics":["elixir","erlang","erlang-otp","framework","gleam","hex","http","web"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/midas","language":"Gleam","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/midas-framework.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-21T08:49:59.000Z","updated_at":"2025-11-17T16:06:25.000Z","dependencies_parsed_at":"2024-08-22T14:10:14.253Z","dependency_job_id":"1b2aadbb-017f-4d12-85e5-06b93ad4cc51","html_url":"https://github.com/midas-framework/midas","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/midas-framework/midas","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midas-framework%2Fmidas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midas-framework%2Fmidas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midas-framework%2Fmidas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midas-framework%2Fmidas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/midas-framework","download_url":"https://codeload.github.com/midas-framework/midas/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midas-framework%2Fmidas/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28431326,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T18:57:19.464Z","status":"ssl_error","status_checked_at":"2026-01-14T18:52:48.501Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","erlang","erlang-otp","framework","gleam","hex","http","web"],"created_at":"2026-01-14T19:06:09.278Z","updated_at":"2026-01-14T19:06:09.944Z","avatar_url":"https://github.com/midas-framework.png","language":"Gleam","readme":"# Midas\n\n[![Package Version](https://img.shields.io/hexpm/v/midas)](https://hex.pm/packages/midas)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/midas/)\n\nMidas tackles the function colouring problem by separating defining and running effectful code.\nIt provides a description of effects, as well functions for composing effectful computation.\n\nThis separation has several benefits:\n- Reuse code across environments\n- Switch out implementations, i.e. choose different HTTP clients\n- No need to use mocks for testing.\n- Allows middleware, for example to add spans for tracing.\n\n```sh\ngleam add midas@2\n```\n\n## Why Midas\n\nAlready sold jump to [Usage](#usage)\n\n### The goal\n\nLet's imagine we have some code that we want to run on JavaScript and the BEAM.\nAlso suppose that this code needs to make http requests.\n\nIdeally we write something like the following.\n\n```gleam\nfn my_func(send){\n  let request_a = request.new() |\u003e request.set_path(\"/a\")\n  let result_a = send(request_a)\n  let assert Ok(a) = result_a\n\n  let request_b = request.new() |\u003e request.set_path(\"/a\")\n  let result_b = send(request_b)\n  let assert Ok(b) = result_b\n\n  todo as \"something with a and b\"\n}\n```\n\nThe simple approach here is to pass the client at runtime so different HTTP clients can be used on each runtime.\n\nGleam has great HTTP clients, there is [gleam_httpc](https://hexdocs.pm/gleam_httpc/) for the BEAM\nand [gleam_fetch](https://hexdocs.pm/gleam_fetch/) for JavaScript.\n\n### The problem\n\nThe problem is function colouring. The two clients have different type signatures for their `send` function.\nhttpc returns a `Result(Response, _)` and fetch returns a `Promise(Result(Response, _))`.\n\nThis difference reflects underlying differences in the runtimes and I think is a good thing to capture this in the type system.\nHowever this difference blocks us from reusing `my_func` as it is.\n\n### The solution\n\nInstead of passing in the client, our code return data type with information on what to do next.\nIn this example our code is either finished or needs to make an HTTP request.\nWe call this returned type `Effect` and it has two variants `Fetch` and `Done`.\n\n```gleam\npub type Effect(t) {\n  Fetch(Request, fn(Result(Response, String)) -\u003e Effect(t))\n  Done(t)\n}\n```\n\nTo write our business logic we make use of `use` to cleanly compose our effects.\n\n```gleam\n\npub fn my_func() {\n  let request_a = request.new() |\u003e request.set_path(\"/a\")\n  use result_a \u003c- Fetch(request_a)\n  let assert Ok(a) = result_a\n\n  let request_b = request.new() |\u003e request.set_path(\"/a\")\n  use result_b \u003c- Fetch(request_b)\n  let assert Ok(b) = result_b\n  Done(todo as \"something with a and b\")\n}\n```\n\nThe `my_func` above only describes the computation but does not run it.\nTo run the function requires a `run` function, there are libraries for running tasks in different environments.\n\nSimple run functions can be implemented as follows:\n\nfor BEAM\n```gleam\npub fn run(effect) {\n  case effect {\n    Fetch(request, resume) -\u003e run(resume(httpc.send(request)))\n    Done(value) -\u003e value\n  }\n}\n```\n\nfor JS\n```gleam\npub fn run(effect) {\n  case effect {\n    Fetch(request, resume) -\u003e {\n      use result \u003c- promise.await(fetch.send(request))\n      run(resume(result))\n    }\n    Done(value) -\u003e promise.resolve(value)\n  }\n}\n```\n\n**Note:** The full midas effect type defines many side effects so your runner will either have to implement them, panic or return a default value for the task.\n\nThat's as far as we take this toy implementation, the rest of the documentation will cover using the actual library.\n\n## Usage\n\nMidas separates defining tasks from running tasks.\nSo first we shall define a task that makes a web request and logs when it has completed.\n\n```gleam\nimport midas/task as t\n\npub fn task() {\n  let request = // ...\n  use response \u003c- t.do(t.fetch(request))\n  use Nil \u003c- t.do(t.log(\"Fetched\"))\n  t.done(Nil)\n}\n```\n\nTo run this in the browser use [midas_browser](https://github.com/midas-framework/midas_browser):\n\n```gleam\nimport midas/browser\n\npub fn main(){\n  use result \u003c- promise.await(browser.run(task()))\n  case result {\n    Ok(_) -\u003e // ...\n    Error(_) -\u003e // ...\n  }\n}\n```\n\n## Testing\n\nBecause we have isolated all side effects testing is easy.\nInstead of using any runner the simplest approach is to assert on each effect in turn.\n\nFirst assert that the effect is as expected, for example that the effect is `Fetch` and that the request has the right path.\nThen resume the program and assert on the next effect, or done if no more effects.\n\n```gleam\nimport midas/effect as e\n\npub fn task_success_test() {\n  let assert e.Fetch(request:, resume:) = task()\n  assert request.path == \"/a/b/c\"\n  \n  let response = response.new(200)\n  let assert e.Log(message:, resume:) = resume(Ok(response))\n  assert messages == \"Fetched\"\n  \n  let assert e.Done(Ok(value)) = resume(Ok(Nil))\n}\n\npub fn network_error_test() {\n  let assert e.Fetch(request:, resume:) = task()\n  assert request.path == \"/a/b/c\"\n\n  let reason = e.NetworkError(\"something bad\")\n  let assert e.Done(Error(reason)) = resume(Error(reason))\n  assert string.contains(snag.pretty_print(reason), \"something bad\")\n}\n```\n\n## Working with errors\n\nThe `midas/task` module assumes that the return value of a task is a result.\nAll the helper functions assume that an error from an effect, such as a network error from a fetch, should be returned as an error from the task.\nThis is easier to work with and all effects are snags.\nHowever this prevents you from recovering or implementing retries.\n\nFor more explicit error handling you can use the `midas/effect` module directly.\n\n## A note of effects\n\nThe `midas/task` module defines an `Effect` type which represents all the effects that can be defined for any task.\nGleam doesn't have an extensible type so the `Effect` type is essentially a single namespace defining all possible effects.\n\nThis is not really a problem because any effect might return an error.\nSo it is always possible to return an error that indicates that the effect is not implemented by a given runner.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidas-framework%2Fmidas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmidas-framework%2Fmidas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidas-framework%2Fmidas/lists"}