{"action":"opened","issue":{"url":"https://api.github.com/repos/gcanti/fp-ts/issues/904","repository_url":"https://api.github.com/repos/gcanti/fp-ts","labels_url":"https://api.github.com/repos/gcanti/fp-ts/issues/904/labels{/name}","comments_url":"https://api.github.com/repos/gcanti/fp-ts/issues/904/comments","events_url":"https://api.github.com/repos/gcanti/fp-ts/issues/904/events","html_url":"https://github.com/gcanti/fp-ts/issues/904","id":466731747,"node_id":"MDU6SXNzdWU0NjY3MzE3NDc=","number":904,"title":"[RFC] Combining `Either`s with different error types","user":{"login":"gcanti","id":878912,"node_id":"MDQ6VXNlcjg3ODkxMg==","avatar_url":"https://avatars1.githubusercontent.com/u/878912?v=4","gravatar_id":"","url":"https://api.github.com/users/gcanti","html_url":"https://github.com/gcanti","followers_url":"https://api.github.com/users/gcanti/followers","following_url":"https://api.github.com/users/gcanti/following{/other_user}","gists_url":"https://api.github.com/users/gcanti/gists{/gist_id}","starred_url":"https://api.github.com/users/gcanti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gcanti/subscriptions","organizations_url":"https://api.github.com/users/gcanti/orgs","repos_url":"https://api.github.com/users/gcanti/repos","events_url":"https://api.github.com/users/gcanti/events{/privacy}","received_events_url":"https://api.github.com/users/gcanti/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2019-07-11T08:07:52Z","updated_at":"2019-07-11T08:07:52Z","closed_at":null,"author_association":"OWNER","body":"**The problem**\r\n\r\n```ts\r\n// example from https://github.com/natefaubion/purescript-checked-exceptions\r\n\r\nimport * as E from 'fp-ts/lib/Either'\r\nimport { pipe } from 'fp-ts/lib/pipeable'\r\n\r\ntype HttpError = 'HttpError'\r\ndeclare function get(url: string): E.Either<HttpError, string>\r\ntype FsError = 'FsError'\r\ndeclare function write(path: string, content: string): E.Either<FsError, void>\r\n\r\ntype Err = HttpError | FsError\r\n\r\nconst program = pipe(\r\n  get('http://purescript.org'), // error: Type '\"HttpError\"' is not assignable to type '\"FsError\"'\r\n  E.chain(content => write('~/purescript.html', content))\r\n)\r\n```\r\n\r\nThis is because `E.chain` is inferred as\r\n\r\n```ts\r\n//                                                                   relevant type here ---v\r\nchain<\"FsError\", string, void>(f: (a: string) => E.Either<\"FsError\", void>): (ma: E.Either<\"FsError\", string>) => E.Either<\"FsError\", void>\r\n```\r\n\r\n**Solutions?**\r\n\r\n1) explicit cast\r\n\r\n```ts\r\nconst writeE1: (path: string, content: string) => E.Either<Err, void> = write\r\n\r\nconst program1 = pipe(\r\n  get('http://purescript.org'),\r\n  E.chain(content => writeE1('~/purescript.html', content))\r\n)\r\n```\r\n\r\n2) explicit `mapLeft`\r\n\r\n```ts\r\nconst widen = E.mapLeft<Err, Err>(e => e)\r\n\r\nconst program2 = pipe(\r\n  get('http://purescript.org'),\r\n  E.chain(content =>\r\n    pipe(\r\n      write('~/purescript.html', content),\r\n      widen\r\n    )\r\n  )\r\n)\r\n```\r\n\r\n3) lifting with `flow` and `mapLeft`\r\n\r\n```ts\r\nimport { flow } from 'fp-ts/lib/function'\r\n\r\nconst writeE2 = flow(\r\n  write,\r\n  widen\r\n)\r\n\r\nconst program3 = pipe(\r\n  get('http://purescript.org'),\r\n  E.chain(content => writeE2('~/purescript.html', content))\r\n)\r\n```\r\n\r\n4) defining a `chain` overload that combines the error types\r\n\r\n```ts\r\nconst flatMap: <E2, A, B>(\r\n  f: (a: A) => E.Either<E2, B>\r\n) => <E1>(ma: E.Either<E1, A>) => E.Either<E1 | E2, B> = E.chain as any\r\n\r\nconst program4 = pipe(\r\n  get('http://purescript.org'),\r\n  flatMap(content => write('~/purescript.html', content))\r\n)\r\n```\r\n\r\nOthers?"}}
{"actor":{"display_login":"gcanti"}}