Skip to content

Commit

Permalink
add ReaderT
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian Hofmann-Hicks committed Nov 5, 2017
1 parent 0662911 commit 7aa845d
Show file tree
Hide file tree
Showing 7 changed files with 999 additions and 60 deletions.
339 changes: 339 additions & 0 deletions src/Reader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
# Reader
```haskell
Reader e a
```
`Reader` is a `Monad` that enables the composition of computations that depend
on a shared environment `(e -> a)`.

```js
const Reader = require('crocks/Reader')
const { ask } = Reader

const concat = require('crocks/pointfree/concat')

// greet :: String -> Reader String String
const greet =
greeting => Reader(name => `${greeting}, ${name}`)

// addFarewell :: String -> Reader String String
const addFarewell = farewell => str =>
ask(env => `${str}${farewell} ${env}`)

// flow :: Reader String String
const flow =
greet('Hola')
.map(concat('...'))
.chain(addFarewell('See Ya'))

flow.runWith('Thomas') // => Hola, Thomas...See Ya Thomas
flow.runWith('Jenny') // => Hola, Jenny...See Ya Jenny
```

## Implements
`Functor`, `Apply`, `Chain`, `Applicative`, `Monad`

## Constructor Methods

### ask
```haskell
Reader.ask :: () -> Reader e e
Reader.ask :: (e -> b) -> Reader e b
```

A construction helper that returns a `Reader` with environment on the right
portion of the `Reader`. `ask` can take a function, that can be used to map the
environment to a different type or value.

```js
const Reader = require('crocks/Reader')
const { ask } = Reader

// add :: Number -> Number -> Number
const add =
x => y => x + y

// Typical constructor
Reader(add(10))
.runWith(56)
//=> 66

// Using `ask` with no function
// (identity on environment)
ask()
.runWith(56)
//=> 56

// Using `ask` with a function
// (map environment before deposit)
ask(add(10))
.runWith(56)
//=> 66
```

### of
```haskell
Reader.of :: a -> Reader e a
```

`of` is used to construct a `Reader` with the right portion populated with it's
argument. `of` essentially will lift a value of type `a` into a `Reader`. Giving
back a `Reader` that is "pointed" to the specific value provided. `of` makes
for a wonderful starting point for some of the more complicated flows.

```js
const Reader = require('crocks/Reader')
const { ask } = Reader

const objOf = require('crocks/helpers/objOf')
const thrush = require('crocks/combinators/reverseApply')

// add :: Number -> Number -> Number
const add =
x => y => x + y

Reader.of(34)
.map(add(6))
.runWith()
//=> 40

Reader.of('Bobby')
.map(objOf('name'))
.runWith()
//=> { name: 'Bobby' }

Reader.of(57)
.chain(x => ask(add).map(thrush(x)))
.runWith(43)
//=> 100
```

### type
```haskell
Reader.type :: () -> String
```

`type` provides a string representation of the type name for a given type in
`crocks`. While it is used mostly internally for law validation, it can be useful
to the end user for debugging and building out custom types based on the standard
`crocks` types. While type comparisons can easily be done manually by calling
`type` on a given type, using the `isSameType` function hides much of the
boilerplate. `type` is available on both the Constructor and the Instance for
convenience.

```js
const Reader = require('crocks/Reader')
const Identity = require('crocks/Identity')

const I = require('crocks/combinators/identity')
const isSameType = require('crocks/predicates/isSameType')

Reader.type() //=> 'Reader'

isSameType(Reader, Reader.of(76)) //=> true
isSameType(Reader, Reader) //=> true
isSameType(Reader, Idenity(0)) //=> false
isSameType(Reader(I), Identity) //=> false

## Instance Methods

### map
```haskell
Reader e a ~> (a -> b) -> Reader e b
```

While the left side, or the environment, of the `Reader` must always be fixed
to the same type, the right side, or value, of the `Reader` may vary. Using `map`
allows a function to be lifted into the `Reader`, mapping the result into the
result of the lifted function.

```js
const Reader = require('crocks/Reader')
const { ask } = Reader

const assign = require('crocks/helpers/assign')
const B = require('crocks/combinators/composeB')
const objOf = require('crocks/helpers/objOf')
const option = require('crocks/pointfree/option')
const prop = require('crocks/Maybe/prop')

// length :: Array -> Number
const length =
x => x.length

ask()
.map(length)
.runWith([ 1, 2, 3 ])
//=> 3

// propOr :: (String, a) -> b -> a
const propOr = (key, def) =>
B(option(def), prop(key))

// lengthObj :: Array -> Object
const lengthObj =
B(objOf('length'), length)

// addLength :: Object -> Redaer Array Object
const addLength = x =>
ask(propOr('list', []))
.map(B(assign(x), lengthObj))

Reader.of({ num: 27 })
.chain(addLength)
.runWith({ list: [ 1, 2, 3 ] })
//=> { length: 3, num: 27 }

```

### ap
```haskell
Reader e (a -> b) ~> Reader e a -> Reader e b
```

[description]

```js
```

### chain
```haskell
Reader e a ~> (a -> Reader e b) -> Reader e b
```

[description]

```js
const Reader = require('crocks/Reader')
const { ask } = Reader

const B = require('crocks/combinators/composeB')
const option = require('crocks/pointfree/option')
const prop = require('crocks/Maybe/prop')

// multiply :: Number -> Number -> Number
const multiply =
x => y => x * y

// add :: Number -> Number -> Number
const add =
x => y => x + y

// propOr :: (String, a) -> b -> a
const propOr = (key, def) =>
B(option(def), prop(key))

// applyScale :: Number -> Reader Object Number
const applyScale = x =>
ask(propOr('scale', 1))
.map(multiply(x))

// applyScale :: Number -> Reader Object Number
const applyOffset = x =>
ask(propOr('offset', 0))
.map(add(x))

// applyTransforms :: Number -> Reader Object Number
const applyTransform = x =>
Reader.of(x)
.chain(applyOffset)
.chain(applyScale)

applyTransform(45)
.runWith({})
//=> 45

applyTransform(45)
.runWith({ offset: 20 })
//=> 65

applyTransform(45)
.runWith({ scale: 2 })
//=> 90

applyTransform(45)
.runWith({ scale: 2, offset: 20 })
//=> 130
```

Monad Transformer
---

## ReaderT
```haskell
Monad m => ReaderT e (m a)
```

### Constructor Methods

#### ask
```haskell
ReaderT.ask :: Monad m => () -> ReaderT e (m e)
ReaderT.ask :: Monad m => (e -> a) -> ReaderT e (m a)
```

[description]

```js
```

#### lift
```haskell
ReaderT.lift :: Monad m => m a -> ReaderT e (m a)
```

[description]

```js
```

#### liftFn
```haskell
ReaderT.liftFn :: Monad m => (a -> m b) -> a -> ReaderT e (m b)
```

[description]

```js
```

#### of
```haskell
ReaderT.of :: a -> Reader e (m a)
```

[description]

```js
```

### Instance Methods

#### map
```haskell
Monad m => ReaderT e (m a) ~> (a -> b) -> Reader e (m b)
```

[description]

```js
```

#### ap
```haskell
Monad m => ReaderT e (m (a -> b)) ~> ReaderT e (m a) -> ReaderT e (m b)
```

[description]

```js
```

#### chain
```haskell
Monad m => ReaderT e (m a) ~> Reader e (a -> ReaderT e (m b)) -> ReaderT e (m b)
```

[description]

```js
```

0 comments on commit 7aa845d

Please sign in to comment.