Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rowtype-yoga/purescript-yoga-om
https://github.com/rowtype-yoga/purescript-yoga-om
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/rowtype-yoga/purescript-yoga-om
- Owner: rowtype-yoga
- License: mit-0
- Created: 2022-06-02T08:34:28.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-07-26T10:14:05.000Z (4 months ago)
- Last Synced: 2024-07-26T11:42:48.269Z (4 months ago)
- Language: PureScript
- Size: 84 KB
- Stars: 11
- Watchers: 2
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-purescript - yoga-om - A powerful general purpose type for writing applications. Includes dependency injection via `Reader`, error `Variant`s and of course `Aff`. (Data types)
README
# 🕉️ (Om)
A powerful general purpose type for writing applications.
For an extensive overview checkout the [tests](./test/Test/Main.purs).
## Installation
```bash
spago install yoga-om
```## Quickstart
Check out the [minimal example](#running-an-om)!
## What can it do?
An `Om` consists of three parts.
A *context*, potential *errors*, and the *value* of a concurrent computation.Conventionally in code, we abbreviate this as `Om ctx errs a`.
### `ctx`: Dependency injection
`Om` supports writing code that `ask`s for dependencies that you only provide
once at the start of the application.```purescript
myOmThatNeedsDbName = do
{ dbName } <- ask
```This is an `Om` of the shape `Om { dbName :: String } _ _`. We are only
concerned about the `ctx` part of `Om` in this section, hence the `_`s.### `errors`: Mix-and match errors
Dealing with failure in `Om` is just wonderful.
### Throwing errors
To throw an error, simply use the `throwError` function with a record that
has the name of the error as its label and the error data as its value:```purescript
myOmThatThrows = Om.throw { myError: "This failed" }
```This is an `Om _ ( myError :: String ) _`
### Handling errors
To catch one or multiple errors use `handleErrors````purescript
myOmHandled = handleErrors { myError: \text -> Console.warn text } myOmThatThrows
```As stated earlier `myOmThatThrows` has type: `Om _ (myError :: String) _`.
By handling all potential `myError` errors we've produced `myOmHandled` which has
the type `Om _ () _`.### Combining errors
A powerful feature of `Om` is that you can easily combine different `Om`
computations that can throw different errors.```purescript
om1 :: forall otherErrors. Om _ ( ioError :: Int | otherErrors ) _
om1 = throw { ioError: -8 }om2 :: forall otherErrors. Om _ ( userError :: String | otherErrors ) _
om2 = throw { userError: "Your last name can't be shorter than 0 characters" }om3 :: forall errs. Om _ ( ioError :: Int, userError :: String | errs ) _
om3 = do
om1
om2
```This means that you can and should only tag a function with the errors it can actually
throw and not the complete set of errors that might happen anywhere in your program.The compiler helps you out with this.
### Working with `Aff` and `Effect`
In order to transform any `Aff a` into an `Om _ _ a` you may use `liftAff`, or `fromAff`.
To do the same with `Effect` you may use `liftEffect`.### Running an `Om`
Let's bring it all back home. Eventually you want to actually run an `Om`.
Most probably at the start of your application, in a `main :: Effect Unit` function for
example.That's the right time to supply the dependencies to your `Om` and to handle any remaining possible errors:
```purescript
module Main whereimport Prelude
import Node.Process (lookupEnv)
import Effect.Class.Console as Console
import Effect (Effect)
import Data.Maybe (Maybe)
import Yoga.Om as Om
import Yoga.Om (Om)main :: Effect Unit
main = do
envName <- lookupEnv "NAME"
greet
# Om.launchOm_
{ envName }
{ exception:
\e -> Console.error ("Unexpected exception: " <> show e)
, nameNotFound:
\_ -> Console.error "Make sure the $NAME env variable is set"
}greet :: Om { envName :: Maybe String } (nameNotFound :: Unit) Unit
greet = do
{ envName } <- Om.ask
name <- envName # Om.note { nameNotFound: unit }
Console.log $ "Welcome " <> name
```### Parallel computations
You can run different `Om`s in parallel.
Either the fastest one that does not error out wins:
```purescript
Om.race [ Om.delay (1.0 # Seconds) *> pure "slow" , pure "fast" ]
```Or you look at all the results and get an `Om _ _ (Array _)`:
```purescript
Om.inParallel
[ Om.delay (9.0 # Milliseconds) *> pure "1"
, Om.delay (1.0 # Milliseconds) *> pure "2"
]
```## FAQ
### What if I need `State` and `Writer`?
Since `Om` combines the powers of `Reader` and supports `Effect`ful computations
it is less clutter (especially on the type signature) to bolt this functionality
on ad-hoc via `Ref`s in the `ctx`### It's so tedious to repeat the labels and keys in `ctx` and `errs`
You will probably prefer to define type aliases:
```purescript
module Main whereimport DB as DB
import Cache as Cache
-- ...
import Type.Row (type (+))myWholeApp :: Om (DB.Ctx + Cache.Ctx ()) (DB.Errs + Cache.Errs ()) Unit
myWholeApp = do
DB.writeToDB "'); DROP TABLE orders;--"```
```purescript
module DB wherewriteToDB :: forall ctx errs. String -> Om (Ctx ctx) (Errs errs) Unit
writeToDB s = do
-- ...
pure unittype Ctx r = (dbCtx :: { port :: Int, hostname :: String } | r)
type Errs r = IOErr + TimeoutErr + r
type IOErr r = ( ioError :: { message :: String, code :: Int, details :: String } | r )
type TimeoutErr r = ( timeout :: { query :: String } | r )```
## Technical behind the scenes info
`Om` is a monad transformer stack built from the stack safe continuation
passing `RWSET` without `State` or `Writer` with errors specialised to
polymorphic `Variant`s.# Special Thanks
We would like to thank all the PureScript contributors, especially the ones whose libraries we depend on.
Special thanks to @reactormonk who has a very similar library ([rave](https://github.com/reactormonk/purescript-rave)) which has the trick to inject variants as single field records instead of the cumbersome `(Proxy :: Proxy "key") value` syntax.