{"size": 3034, "ext": "js", "max_stars_repo_path": "Section 2/3.2.5.code.js", "max_stars_repo_name": "PacktPublishing/-Mastering-Your-Web-Application-Using-Functional-Javascript", "max_stars_repo_stars_event_min_datetime": "2021-03-06T23:41:57.000Z", "max_stars_repo_stars_event_max_datetime": "2021-10-01T22:06:04.000Z", "max_issues_repo_path": "Section 2/3.2.5.code.js", "max_issues_repo_name": "PacktPublishing/-Mastering-Your-Web-Application-Using-Functional-Javascript", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "Section 2/3.2.5.code.js", "max_forks_repo_name": "PacktPublishing/-Mastering-Your-Web-Application-Using-Functional-Javascript", "max_forks_repo_forks_event_min_datetime": "2019-11-07T00:22:02.000Z", "max_forks_repo_forks_event_max_datetime": "2020-02-24T08:59:14.000Z"}
{"max_stars_count": 2, "max_issues_count": null, "max_forks_count": 2, "avg_line_length": 18.9625, "max_line_length": 71, "alphanum_fraction": 0.5405405405}
const R = require('ramda')
const { log } = console
const { map, compose, equals } = R


class Either {
  
  // of :: (Either m) => a -> m a
  static of (value) {
    return new Right(value)
  }
  
  // toEither :: (Either m) => a -> m a
  static toEither (value) {
    return value == null ? new Left(value) : new Right(value)
  }
  
  // encase :: (a, () -> b) -> Either a b
  static encase (leftValue, rightFn) {
    try {
      return Right.of(rightFn())
    }
    catch(e) {
      return Left.of(leftValue)
    }
  }

  constructor (value) {
    if (this.constructor === Either) {
      throw new Error('Either should not be used as constructor.')
    }
      this.value = value
  }
  
  // bimap :: (Either e) => e a b ~> (a -> c, b -> d) -> e c d
  bimap (leftFn, rightFn) {
    return this.isRight ? 
      Right.fmap(rightFn) : Left.of(leftFn(this.value))
  }

  // alt :: (Either e) => e a b ~> (e a b) -> e a b
  alt (alternative) {
    if (Either.isEither(alternative)) {
      return this.isRight ? this : alternative
    }
    throw new Error('Either#alt expects an Either as an argument')
  }
}


class Right extends Either {
  
  get isRight() { return true }
  get isLeft() { return false }

  // fmap :: (Either e) => e a b ~> (b -> c) -> e a c
  fmap (fn) {
    return new Right(fn(this.value))
  }
  
  // ap :: (Either e) => e a b ~> e a (b -> c) -> e a c
  ap (aFn) { 
    //return this.fmap(aFn.value)
    return aFn.chain(fn => Right.of(fn(this.value)))
  }

  // chain :: (Either e) => e a b ~> (b -> e a c) -> e a c
  chain (fnA) {
    return fnA(this.value)
  }

}


class Left extends Either  {
  
  get isRight() { return false }
  get isLeft() { return true }
  
  static of (value) {
    return new Left(value)
  }

  // fmap :: (Either e) => e a b ~> (b -> c) -> e a c
  fmap (_) {
    return this
  }
  
  // ap :: (Either e) => e a b ~> e a (b -> c) -> e a c
  ap (_) { 
    return this
  }

  // chain :: (Either e) => e a b ~> (b -> e a c) -> e a c
  chain (_) {
    return this 
  }

}



const id = x => x
const f = n => n * 1.07 + 10
const g = n => 100 - n
const fg = compose(g, f)

const fa = new Right('Apply World')
const mf = n => Right.of(f(n))
const mg = n => Right.of(g(n))

const toEither = Either.toEither
/*
log(
  fa.ap(new Right(R.toUpper))
)

log(equals(
  Right.of(100).ap(Right.of(f))
  ,
  Right.of(f).ap(Right.of(fn => fn(100)))
))

log(equals(
  Right.of(100).fmap(mf).value.fmap(mg).value,
  Right.of(100).chain(mf).chain(mg)
))

log(Right.of(101).chain(Right.of))

log(
  toEither(100).ap(toEither(x => x + 100)),
  Left.of().ap(toEither(x => x + 100))
)

log(
  Right.of(':)').ap(toEither(null))
)
*/

log(
  Right.of(100).chain(x => Right.of(x * 100)),
)
log(
  Right.of(100).chain(x => Left.of('God Save the Queen!'))
)


const { encase } = Either
// Returns Left.of('That is not JSON...')
log(
  encase('That is not JSON Jason!', () => JSON.parse('{ a: 10 }'))
)
// Returns Right.of(object)
log(
  encase('That is not JSON Jason!', () => JSON.parse('{ "a": "10" }')),
)


