Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's your monad stack? #251

Closed
gcanti opened this issue Oct 14, 2017 · 10 comments
Closed

What's your monad stack? #251

gcanti opened this issue Oct 14, 2017 · 10 comments

Comments

@gcanti
Copy link
Owner

gcanti commented Oct 14, 2017

I wonder if it's worth consolidating a common stack, something like

type ReaderTaskEither<E, L, A> = (e: E) => TaskEither<L, A>
@raveclassic
Copy link
Collaborator

I'm only in the middle of integration of fp-ts into our codebase. We are using Option, Either and some custom ADTs right now. The plans are to use Validation also.

@gcanti
Copy link
Owner Author

gcanti commented Oct 15, 2017

@raveclassic I mean when you have to nest monads, let me show you an example.

Say you have a simple API like this

import { Task } from 'fp-ts/lib/Task'
import axios from 'axios'

/** If successful, returns the invoice id */
const purchase1 = (userId: string, productId: string): Task<string> =>
  new Task(() => axios.post(`https://mysite/api/user/${userId}/purchase/${productId}`, {}).then(res => res.data))

There's no error handling though, so you switch to TaskEither (already consolidated in fp-ts)

type PurchaseError = { type: 'UserNotFound' } | { type: 'ProductNotFound' } | { type: 'PurchaseNotAllowed' }

import { TaskEither, tryCatch } from 'fp-ts/lib/TaskEither'

const purchase2 = (userId: string, productId: string): TaskEither<PurchaseError, string> =>
  tryCatch<PurchaseError, string>(
    () => axios.post(`https://mysite/api/user/${userId}/purchase/${productId}`, {}).then(res => res.data),
    reason => {
      // error handling here...
      return { type: 'PurchaseNotAllowed' }
    }
  )

The url is hard-coded though, and here it comes ReaderTaskEither

class ReaderTaskEither<E, L, A> {
  constructor(readonly run: (e: E) => TaskEither<L, A>) {}
  /* ... */
}

type AppConfig = {
  apiRoot: string
}

const purchase3 = (userId: string, productId: string): ReaderTaskEither<AppConfig, PurchaseError, string> =>
  new ReaderTaskEither(config =>
    tryCatch<PurchaseError, string>(
      () => axios.post(`${config.apiRoot}/user/${userId}/purchase/${productId}`, {}).then(res => res.data),
                          // ^ no more hard-coded
      reason => {
        // error handling here...
        return { type: 'PurchaseNotAllowed' }
      }
    )
  )

purchase3('123', '456')
  .run({ apiRoot: 'https://mysite/api' })
  .value.run()
  .then(e => e.fold(error => console.error(error), invoiceId => console.log(invoiceId)))

@gcanti
Copy link
Owner Author

gcanti commented Oct 15, 2017

A reference implementation ReaderTaskEither

@sledorze
Copy link
Collaborator

That s an interesting question and to be honnest, i ve not introduced yet monad transformers to the team.
I ll do it but i m lurking at the different task/io libraries out there..
I m concerned about correctness, performance and leaks and know there s a lot of efforts in funfix atm.
Also i ve not checked (i m late on it) how well or not fp-ts and funfix integrate with each other (diff hkt impl may be a show stopper)

@gcanti
Copy link
Owner Author

gcanti commented Oct 15, 2017

I m concerned about correctness, performance and leaks and know there s a lot of efforts in funfix atm

@sledorze that's interesting and something I would like to dig deeper into, could you please point me to some resources?

how well or not fp-ts and funfix integrate with each other

Not sure, that's interesting as well. I'll do some experiments and open a new issue

@sledorze
Copy link
Collaborator

sledorze commented Oct 15, 2017

@gcanti you may find some insight on his creator (alex) blog: https://alexn.org/blog/2017/10/11/javascript-promise-leaks-memory.html
For information he his also behind the Monix library in Scala.
Heres the gitter ofthe project: https://gitter.im/funfix/funfix
Basically my beliefs are based on this guy, what he has achieved and his willing to replicate that on node/browser.
And to replicate his words:

alexelcu [3:38 PM] 
I don’t have numbers yet. `IO` is a port of Monix Task, which is currently the fastest implementation for Scala (versus Cats IO, FS2 and Scalaz 7) and so this `IO` has a really good encoding.

If it’s not amongst the fastest, then I’ll treat that as a bug that needs to be fixed.

About performance you can get some infos from here (execution strategies):
funfix/funfix.js#37

That may be a good start.

@sledorze
Copy link
Collaborator

@gcanti Ok I'm starting to use a stack based on futureEither and readerFutureEither (Future from funfix) - derived from the advices you gave.
It works very well.
Not sure if it could belong to that repo anyway as it would create a non desirable dependency.

@gcanti
Copy link
Owner Author

gcanti commented Nov 1, 2017

It works very well

@sledorze really glad to hear that: I tried to build fp-ts so it can be expanded, as I did with fp-ts-fluture or fp-ts-rxjs, your move to funfix is a good proof of that

Not sure if it could belong to that repo anyway

Maybe you could publish a package

@RPallas92
Copy link

But since, ReaderTaskEither implements Monad3, we can't use it in the MTL example.

Is there any workaround for this? @gcanti

@gcanti
Copy link
Owner Author

gcanti commented Apr 26, 2018

@RPallas92 with more overloadings (alas doing MTL-style in TypeScript is super verbose) see #410

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants