DEV Community

Discussion on: Getting started with fp-ts: Reader

Collapse
 
vicrac profile image
Michał Kaczanowicz

I'd say there'e no magic in here - in FP all functions have to be referentially transparent at the end of the day i.e. all arguments have to be passed directly! What I think is crucial in here is the conceptual "turning inside-out" (or rather "bottom-up", should I say) - instead of passing props/arguments down multiple levels, we pass a function consuming these props (Reader) multiple levels up, in an almost implicit way. When the Reader reaches "the surface", we can pass its dependencies directly.

Collapse
 
macsikora profile image
Pragmatic Maciej

Yes I see this in that way also. The whole idea here is in dependency argument in the end and not in the beginning.

In my comment I was more addressing the whole additional idealogy over that, like special typings and using some pipe, ask, chain. I see these as only a fog and complexity over really simple but powerful concept.

Thread Thread
 
gcanti profile image
Giulio Canti

There's a benefit in using the monadic interface though: software is written in a uniform style, regardless of the effect.

You can think of Reader as an effect, muck like Either or Task.

Let's say you have the following snippet

import * as E from 'fp-ts/lib/Either'
import { pipe } from 'fp-ts/lib/pipeable'

declare function f(s: string): E.Either<Error, number>
declare function g(n: number): boolean
declare function h(b: boolean): E.Either<Error, Date>

// composing `f`, `g`, and `h` -------------v---------v-----------v
const result = pipe(E.right('foo'), E.chain(f), E.map(g), E.chain(h))

const pointFreeVersion = flow(f, E.map(g), E.chain(h))
Enter fullscreen mode Exit fullscreen mode

and at some point you must refactor f to

import * as RE from 'fp-ts/lib/ReaderEither'

interface Dependencies {
  foo: string
}

declare function f(s: string): RE.ReaderEither<Dependencies, Error, number>
Enter fullscreen mode Exit fullscreen mode

result and pointFreeVersion must be refactored as well, fortunately you can use ReaderEither's monadic interface

// before 
const result = pipe(E.right('foo'), E.chain(f), E.map(g), E.chain(h))

const pointFreeVersion = flow(f, E.map(g), E.chain(h))

// after
const result = pipe(
  RE.right('foo'),
  RE.chain(f),
  RE.map(g),
  RE.chain(b => RE.fromEither(h(b)))
)

const pointFreeVersion = flow(
  f,
  RE.map(g),
  RE.chain(b => RE.fromEither(h(b)))
)
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
vicrac profile image
Michał Kaczanowicz

There's a benefit in using the monadic interface though: software is written in a uniform style, regardless of the effect.

I was just about to say it: I think main benefit is (ironically) readability - by saying explicitly Reader<Dependencies, string> you clearly communicate your intent (assuming others know the concept as well, of course 😄).

Thread Thread
 
gcanti profile image
Giulio Canti

That's right, you write programs by composing kleisli arrows A -> M<B>, for some effect M.

So Reader (or ReaderEither, ReaderTaskEither, Option, Either, etc...) is just one of the possible effects