Provide a Prism?
Not sure if this is desired or out of scope, but it might be handy!
A Prism is an optic used to select part of a Sum type (source) so it seems like a natural fit in this ADT library.
We would probably want to avoid a dependency on monocle-ts, so we'd copy and paste the Prism type. It seems unorthodox, but it's how Elm implemented prisms with the original RemoteData type
Spec:
import { ADT, prism } from 'ts-adt'
import * as Op from 'monocle-ts/lib/Op'
import { pipe } from 'fp-ts/function'
import * as O from 'fp-ts/Option'
type Xor<A, B> = ADT<{
nothing: {};
left: { value: A };
right: { value: A };
}>;
const getId: O.Option<string> = pipe(
Op.id<Xor<number, { id: string }>>(),
Op.composePrism(prism('right')),
Op.prop('value'),
Op.prop('id'),
op => op.getOption({_type: 'right', value: { id: '3' } })
)
assert.deepStrictEqual(getId, O.some('3'))
Implementation:
import { pipe, identity } from 'fp-ts/function'
import * as O from 'fp-ts/Option'
interface Prism<S, A> {
readonly getOption: (s: S) => O.Option<A>
readonly reverseGet: (a: A) => S
}
const makePrism = <TagName extends string>(
tagName: TagName
) => <ADT extends { [tag in TagName]: string }, Tag extends ADT[TagName]>(
tag: Tag
): Prism<ADT, Extract<ADT, { [t in TagName]: Tag }>> => ({
getOption: O.fromPredicate(
(s): s is Extract<ADT, { [t in TagName]: Tag }> =>
s[tagName] === tag
),
reverseGet: identity,
})
const prism = makePrism('_type')
So, another note is that ts-adt doesn't depend on fp-ts, so copy/pasting the Prism interface wouldn't be enough, since we'd still need access to Option... I'm thinking it might make sense to make a ts-adt-fp-ts package that includes fp-ts, monocle and any other fp-ts ecosystem libs
Oh ok - my mistake. I think it could also be ok to copy/paste the Option interface by the same rationale, esp since that seems unlikely to change, but I understand that it feels gross and might introduce bugs.
Do you know of/have ideas for other stuff that might find a home in a ts-adt-fp-ts package?