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

Modules / Do.ts / Example: fp-ts-to-the-max-II.ts #37

Closed
civilizeddev opened this issue Dec 30, 2019 · 1 comment
Closed

Modules / Do.ts / Example: fp-ts-to-the-max-II.ts #37

civilizeddev opened this issue Dec 30, 2019 · 1 comment

Comments

@civilizeddev
Copy link

civilizeddev commented Dec 30, 2019

📖 Documentation

  • I rewrote fp-ts-to-the-max-II.ts using Do.
  • I think this will help people who are just beginning to use Do.
  • It covers Do's bind, bindL, do, doL, sequenceS, return methods.
import { URIS, Kind } from 'fp-ts/lib/HKT'
import { createInterface } from 'readline'
import { flow, constVoid } from 'fp-ts/lib/function'
import * as C from 'fp-ts/lib/Console'
import * as M from 'fp-ts/lib/Monad'
import * as O from 'fp-ts/lib/Option'
import * as R from 'fp-ts/lib/Random'
import * as T from 'fp-ts/lib/Task'
import { Do } from 'fp-ts-contrib/lib/Do'

// type classes

interface Program<F extends URIS> extends M.Monad1<F> {
  finish: <A>(a: A) => Kind<F, A>
}

interface Console<F extends URIS> {
  putStrLn: (message: string) => Kind<F, void>
  getStrLn: Kind<F, string>
}

interface Random<F extends URIS> {
  nextInt: (upper: number) => Kind<F, number>
}

interface Main<F extends URIS> extends Program<F>, Console<F>, Random<F> {}

// instances

const programTask: Program<T.URI> = {
  ...T.task,
  finish: T.of,
}

/**
 * read from standard input
 */
const getStrLn: T.Task<string> = () =>
  new Promise(resolve => {
    const rl = createInterface({
      input: process.stdin,
      output: process.stdout,
    })
    rl.question('> ', answer => {
      rl.close()
      resolve(answer)
    })
  })

/**
 * write to standard output
 */
const putStrLn = flow(C.log, T.fromIO)

const consoleTask: Console<T.URI> = {
  getStrLn,
  putStrLn,
}

const randomTask: Random<T.URI> = {
  nextInt: upper => T.fromIO(R.randomInt(1, upper)),
}

// game

/**
 * parse a string to an integer
 */
function parse(s: string): O.Option<number> {
  const i = +s
  return isNaN(i) || i % 1 !== 0 ? O.none : O.some(i)
}

function main<F extends URIS>(F: Main<F>): Kind<F, void> {
  // ask something and get the answer
  const ask = (question: string): Kind<F, string> =>
    Do(F)
      .do(F.putStrLn(question))
      .bind('answer', F.getStrLn)
      .return(({ answer }) => answer)

  const shouldContinue = (name: string): Kind<F, boolean> =>
    Do(F)
      .bind('answer', ask(`Do you want to continue, ${name} (y/n)?`))
      .bindL('result', ({ answer }) => {
        switch (answer.toLowerCase()) {
          case 'y':
            return F.of(true)
          case 'n':
            return F.of(false)
          default:
            return shouldContinue(name)
        }
      })
      .return(({ result }) => result)

  const gameLoop = (name: string): Kind<F, void> =>
    Do(F)
      // run `n` tasks in parallel
      .sequenceS({
        guess: ask(`Dear ${name}, please guess a number from 1 to 5`),
        secret: F.nextInt(5),
      })
      .doL(({ guess, secret }) =>
        O.fold(
          () => F.putStrLn('You did not enter an integer!'),
          (x: number) =>
            x === secret
              ? F.putStrLn(`You guessed right, ${name}!`)
              : F.putStrLn(
                  `You guessed wrong, ${name}! The number was: ${secret}`,
                ),
        )(parse(guess)),
      )
      .bind('shouldContinue', shouldContinue(name))
      .bindL('result', ({ shouldContinue }) =>
        shouldContinue ? gameLoop(name) : F.of<void>(undefined),
      )
      .return(({ result }) => result)

  return Do(F)
    .bind('name', ask('What is your name?'))
    .doL(({ name }) => F.putStrLn(`Hello, ${name} welcome to the game!`))
    .doL(({ name }) => gameLoop(name))
    .return(constVoid)
}

export const mainTask = main({
  ...programTask,
  ...consoleTask,
  ...randomTask,
})

mainTask()

Thank you.

@IMax153
Copy link
Collaborator

IMax153 commented Sep 21, 2020

Hey @civilizeddev! With the introduction of do notation to all monadic type classes in the main fp-ts repo in the form of the bind and bindTo helpers, I think we can close this issue in favor of an updated PR in the main fp-ts repository that utilizes the new do notation helpers.

For now, I will go ahead and close this issue. Please let me know if there is any objection to my suggestion above and we can reopen!

@IMax153 IMax153 closed this as completed Sep 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants