{"id":18335062,"url":"https://github.com/cmeeren/cvdm.errorhandling","last_synced_at":"2025-07-03T17:09:34.107Z","repository":{"id":87810507,"uuid":"124245701","full_name":"cmeeren/Cvdm.ErrorHandling","owner":"cmeeren","description":"[DEPRECATED, use FsToolkit.ErrorHandling] AsyncResult and Result computation expressions and helper functions for error handling in F#","archived":false,"fork":false,"pushed_at":"2019-04-23T07:10:41.000Z","size":156,"stargazers_count":43,"open_issues_count":1,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-21T17:08:43.090Z","etag":null,"topics":["async","asyncresult","computation-expressions","error-handling","fsharp","result"],"latest_commit_sha":null,"homepage":"","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cmeeren.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-03-07T14:12:28.000Z","updated_at":"2021-12-16T23:07:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"28d164d2-a7da-49bc-86bf-e685d60c742b","html_url":"https://github.com/cmeeren/Cvdm.ErrorHandling","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmeeren%2FCvdm.ErrorHandling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmeeren%2FCvdm.ErrorHandling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmeeren%2FCvdm.ErrorHandling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmeeren%2FCvdm.ErrorHandling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cmeeren","download_url":"https://codeload.github.com/cmeeren/Cvdm.ErrorHandling/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247435043,"owners_count":20938530,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["async","asyncresult","computation-expressions","error-handling","fsharp","result"],"created_at":"2024-11-05T19:53:18.596Z","updated_at":"2025-04-06T04:33:44.739Z","avatar_url":"https://github.com/cmeeren.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"DEPRECATED, REPLACED BY FsToolkit.ErrorHandling\n===============================================\n\nThis library has been merged into [FsToolkit.ErrorHandling](https://github.com/demystifyfp/FsToolkit.ErrorHandling/) and is now deprecated. Original readme follows below.\n\nCvdm.ErrorHandling\n==================\n\n[![NuGet](https://img.shields.io/nuget/dt/Cvdm.ErrorHandling.svg?style=flat)](https://www.nuget.org/packages/Cvdm.ErrorHandling/) [![Build status](https://ci.appveyor.com/api/projects/status/r4pe0qp93fnjenoc/branch/master?svg=true)](https://ci.appveyor.com/project/cmeeren/cvdm-errorhandling/branch/master)\n\n*`asyncResult` and `result` computation expressions and helper functions for error handling in F#.*\n\nSummary\n-------\n\n* Use `result { }` for monadic error handling\n* Use `asyncResult { }` instead of `result { }` if you need to call functions returning `Async\u003c_\u003e`\n* The library is auto-opened, so you don’t need to `open Cvdm.ErrorHandling` (except when using Fable)\n* The library includes several helper functions in the `Result` and `AsyncResult` modules, see [Helpers.fs](https://github.com/cmeeren/Cvdm.ErrorHandling/blob/master/Cvdm.ErrorHandling/Helpers.fs) for signatures and details\n\nThe `result` computation expression\n-----------------------------------\n\nThe `result` computation expression simplifies synchronous error handling using F#'s `Result\u003c_,_\u003e` type. A single computation expression must have a single error type, and helper functions simplify transforming return values to a `Result` with the needed error type. Here's an example:\n\n```F#\n// Given the following functions:\n//   tryGetUser: string -\u003e User option\n//   isPwdValid: string -\u003e User -\u003e bool\n//   authorize: User -\u003e Result\u003cunit, AuthError\u003e\n//   createAuthToken: User -\u003e AuthToken\n\n// Here's how a simple login usecase can be written:\n\ntype LoginError = InvalidUser | InvalidPwd | Unauthorized of AuthError\n\nlet login (username: string) (password: string) : Result\u003cAuthToken, LoginError\u003e =\n  result {\n    // requireSome unwraps a Some value or gives the specified error if None\n    let! user = username |\u003e tryGetUser |\u003e Result.requireSome InvalidUser\n\n    // requireTrue gives the specified error if false\n    do! user |\u003e isPwdValid password |\u003e Result.requireTrue InvalidPwd\n\n    // Error value is wrapped/transformed (Unauthorized has signature AuthError -\u003e LoginError)\n    do! user |\u003e authorize |\u003e Result.mapError Unauthorized\n\n    return user |\u003e createAuthToken\n  }\n```\n\nThe expression above will stop at and return the first error.\n\n\n\nThe `asyncResult` computation expression\n----------------------------------------\n\nThe `asyncResult` computation expression is more or less identical to the `result` expression except it's centered around `Async\u003cResult\u003c_,_\u003e\u003e `, with overloads supporting `Result\u003c_,_\u003e` (which is wrapped in `Async`) and `Async\u003c_\u003e` (whose value is wrapped using `Result.Ok`). Overloads also exists for `Task\u003c_\u003e` and `Task`. In other words, on the right side of `let!`, `do!` etc. you can have `Async\u003cResult\u003c_,_\u003e\u003e`, `Async\u003c_\u003e`, `Result\u003c_,_\u003e`, `Task\u003cResult\u003c_,_\u003e\u003e`, `Task\u003c_\u003e`, or `Task`.\n\n`asyncResult` is intended to be almost a drop-in replacement of `result`. If you have a `result` expression and you need to unwrap `Async` values inside it, just change it to `asyncResult`. (The consumers of the changed expression will of course now need to change since it's `Async\u003cResult\u003c_,_\u003e\u003e` instead of `Result\u003c_,_\u003e`, but the contents of the expression itself should not need to change just from this switch.)\n\n`AsyncResult` has more or less the same helper functions as `Result`. Here's the same example as above, with some signatures modified a bit:\n\n```F#\n// Given the following functions:\n//   tryGetUser: string -\u003e Async\u003cUser option\u003e                 \u003c- this is async now\n//   isPwdValid: string -\u003e User -\u003e bool                       \u003c- still synchronous\n//   authorize: User -\u003e Async\u003cResult\u003cunit, AuthError\u003e\u003e        \u003c- this is async now\n//   createAuthToken: User -\u003e Result\u003cAuthToken, TokenError\u003e   \u003c- still synchronous, but can now fail\n\ntype LoginError = InvalidUser | InvalidPwd | Unauthorized of AuthError | TokenErr of TokenError\n\nlet login (username: string) (password: string) : Async\u003cResult\u003cAuthToken, LoginError\u003e\u003e =\n  asyncResult {\n    // tryGetUser is async, so we use the function from the AsyncResult module\n    let! user = username |\u003e tryGetUser |\u003e AsyncResult.requireSome InvalidUser\n\n    // isPwdValid returns Result, so we still use the function from the `Result` module\n    do! user |\u003e isPwdValid password |\u003e Result.requireTrue InvalidPwd\n\n    // authorize is async, so again we use AsyncResult instead of Result\n    do! user |\u003e authorize |\u003e AsyncResult.mapError Unauthorized\n\n    // createAuthToken evaluates to a Result, but using return! (and other bang keywords)\n    // it's automatically wrapped in Async to be compatible with the computation expression\n    return! user |\u003e createAuthToken |\u003e Result.mapError TokenErr\n  }\n```\n\nThe helper functions\n--------------------\n\nThe library includes several helper functions in the `Result` and `AsyncResult` modules, see [Helpers.fs](https://github.com/cmeeren/Cvdm.ErrorHandling/blob/master/Cvdm.ErrorHandling/Helpers.fs) for signatures and details.\n\n## A note on namespace imports\n\nThis library is tagged with the `AutoOpen` attribute. Referencing this library will automatically open the `Cvdm.ErrorHandling` namespace. This means you will have access to this library's computation expressions and helper methods in every file without any explicit `open` statements.\n\nShould you have a value or function in your code with the same name as a value or function in this library (e.g. `result`/`Result.defaultValue`), your definition will take precedence. You may override this behaviour in a file and use the library's definition by explicitly `open`ing `Cvdm.ErrorHandling` again.\n\n## A note on type inference\n\nDue to limitations in the F# type inference system when overloads are involved (see [Microsoft/visualfsharp#4472](https://github.com/Microsoft/visualfsharp/issues/4472)), you might sometimes have to add explicit type annotations. For example, the following expression\n\n```F#\nasyncResult {\n  let! str = asyncResult { return \"\" }\n  return str.Length\n         ^^^^^^^^^^ \u003c- error FS0072\n}\n```\n\nwill give you an error on `str.Length` saying\n\n`error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.`\n\nThe solution is to annotate `str`:\n\n```F#\nlet! (str: string) = asyncResult { return \"\" }\n```\n\nThings seem to work fine when the right-hand side is `Async\u003c_\u003e` or `Result\u003c_,_\u003e`.\n\n## Using this library in a Fable project\n\nThis library can be used with [Fable](http://fable.io). In Fable projects, `AutoOpen` instructions as used by this library can fail.  In this case, just add an explicit open statement (`open Cvdm.ErrorHandling`).\n\n## A technical note on overload resolution\n\n(Aside from the type inference limiation mentioned above, it all \"just works\" as you'd want; this is for the curious.)\n\nIf you have an expression of type `Async\u003cResult\u003c_,_\u003e\u003e`, then the compiler normally doesn't know how to choose between the overloads taking `Async\u003cResult\u003c_,_\u003e\u003e` and `Async\u003c_\u003e` since both are compatible. I have solved this by having the `Async\u003c_\u003e` members as extension methods. The compiler will then give these lower priority. I consider this bit of \"magic\" completely acceptable in this situation since\n\n1. the resulting behavior is intuitive and exactly what you want,\n2. it still follows strict rules (as defined above), and\n3. the whole point of this computation expression is to make your life easier when handling asynchronous errors. The alternative is to explicitly wrap all `Async\u003cResult\u003c_,_\u003e\u003e` expressions in a wrapper type to make overload resolution work (like Chessie does) which IMHO doesn't really add any meaningful clarity.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmeeren%2Fcvdm.errorhandling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcmeeren%2Fcvdm.errorhandling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmeeren%2Fcvdm.errorhandling/lists"}