-
-
Notifications
You must be signed in to change notification settings - Fork 502
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
Compositions & pipe #925
Comments
import * as array from 'fp-ts/lib/Array'
import { Monad1 } from 'fp-ts/lib/Monad'
import { Option, some, none } from 'fp-ts/lib/Option'
import * as optionT from 'fp-ts/lib/OptionT'
import { pipe, pipeable } from 'fp-ts/lib/pipeable'
// a Monad instance for `Array<Option<A>>`...
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
ArrayOption: Array<Option<A>>
}
}
const arrayOption: Monad1<'ArrayOption'> = {
URI: 'ArrayOption',
...optionT.getOptionM(array.array)
}
// ...getting pipeable functions...
const { ap, apFirst, apSecond, chain, chainFirst, flatten, map } = pipeable(arrayOption)
// ...usage
pipe(
arrayOption.of(1),
map(v => v + 1),
chain(n => (n > 0 ? [some(n + 1), some(n - 1)] : [none]))
) |
Thanks! I was worried that was the answer.. Out of curiosity do you think there's TypeScript features coming any time soon that could make it less verbose? |
@gcanti Shouldn't we reconsider changing typeclasses signatures to be always data-last? I have exactly the same problem right now trying to write tests for new |
Just played a bit, looks nice: import { HKT, Kind, URIS } from 'fp-ts/lib/HKT';
import { pipe } from 'fp-ts/lib/pipeable';
///////
export interface Functor<F> {
readonly URI: F;
readonly map: <A, B>(f: (a: A) => B) => (fa: HKT<F, A>) => HKT<F, B>;
}
export interface Functor1<F extends URIS> {
readonly URI: F;
readonly map: <A, B>(f: (a: A) => B) => (fa: Kind<F, A>) => Kind<F, B>;
}
///////
export interface Nothing {
readonly _tag: 'Nothing';
}
export const nothing: Maybe<never> = {
_tag: 'Nothing',
};
export interface Just<A> {
readonly _tag: 'Just';
readonly value: A;
}
export const just = <A>(value: A): Maybe<A> => ({
_tag: 'Just',
value,
});
export type Maybe<A> = Nothing | Just<A>;
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Maybe: Maybe<A>;
}
}
export const maybe: Functor1<'Maybe'> = {
URI: 'Maybe',
map: f => fa => (fa._tag === 'Just' ? just(f(fa.value)) : nothing),
};
//////////
export type MaybeT<M, A> = HKT<M, Maybe<A>>;
export type MaybeT1<M extends URIS, A> = Kind<M, Maybe<A>>;
export interface MaybeM<M> {
map: <A, B>(f: (a: A) => B) => (ma: MaybeT<M, A>) => MaybeT<M, B>;
}
export interface MaybeM1<M extends URIS> {
map: <A, B>(f: (a: A) => B) => (ma: MaybeT1<M, A>) => MaybeT1<M, A>;
}
export function getMaybeM<M extends URIS>(M: Functor1<M>): MaybeM1<M>;
export function getMaybeM<M>(M: Functor<M>): MaybeM<M>;
export function getMaybeM<M>(M: Functor<M>): MaybeM<M> {
return {
map: f => M.map(maybe.map(f)),
};
}
////////
export type Box<A> = [A];
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Box: Box<A>;
}
}
export const box: Functor1<'Box'> = {
URI: 'Box',
map: f => fa => [f(fa[0])],
};
//////////
const M = getMaybeM(box);
const inc = (n: number) => n + 1;
const test1: Maybe<number> = pipe(
just(123),
maybe.map(inc),
);
const test2: Box<Maybe<number>> = pipe(
[just(123)],
M.map(inc),
); |
How would you make compositions like import * as O from 'fp-ts/lib/Option';
import * as T from 'fp-ts/lib/Task';
O.option.traverse(T.task); |
@OliverJAsh @gcanti already tried #823 (comment) |
@OliverJAsh import { HKT, Kind, URIS } from 'fp-ts/lib/HKT';
import { pipe } from 'fp-ts/lib/pipeable';
///////
export interface Functor<F> {
readonly URI: F;
readonly map: <A, B>(f: (a: A) => B) => (fa: HKT<F, A>) => HKT<F, B>;
}
export interface Functor1<F extends URIS> {
readonly URI: F;
readonly map: <A, B>(f: (a: A) => B) => (fa: Kind<F, A>) => Kind<F, B>;
}
export interface Apply<F> extends Functor<F> {
readonly ap: <A, B>(fab: HKT<F, (a: A) => B>) => (fa: HKT<F, A>) => HKT<F, B>;
}
export interface Apply1<F extends URIS> extends Functor1<F> {
readonly ap: <A, B>(fab: Kind<F, (a: A) => B>) => (fa: Kind<F, A>) => Kind<F, B>;
}
export interface Applicative<F> extends Apply<F> {
readonly of: <A>(a: A) => HKT<F, A>;
}
export interface Applicative1<F extends URIS> extends Apply1<F> {
readonly of: <A>(a: A) => Kind<F, A>;
}
export interface Traverse<T> {
<F extends URIS>(F: Applicative1<F>): <A, B>(f: (a: A) => Kind<F, B>) => (ta: HKT<T, A>) => Kind<F, HKT<T, B>>;
<F>(F: Applicative<F>): <A, B>(f: (a: A) => HKT<F, B>) => (ta: HKT<T, A>) => HKT<F, HKT<T, B>>;
}
export interface Traverse1<T extends URIS> {
<F extends URIS>(F: Applicative1<F>): <A, B>(f: (a: A) => Kind<F, B>) => (ta: Kind<T, A>) => Kind<F, Kind<T, B>>;
<F>(F: Applicative<F>): <A, B>(f: (a: A) => HKT<F, B>) => (ta: Kind<T, A>) => HKT<F, Kind<T, B>>;
}
export interface Traversable<T> extends Functor<T> {
readonly traverse: Traverse<T>;
}
export interface Traversable1<T extends URIS> extends Functor1<T> {
readonly traverse: Traverse1<T>;
}
///////
export interface Nothing {
readonly _tag: 'Nothing';
}
export const nothing: Maybe<never> = {
_tag: 'Nothing',
};
export interface Just<A> {
readonly _tag: 'Just';
readonly value: A;
}
export const just = <A>(value: A): Maybe<A> => ({
_tag: 'Just',
value,
});
export type Maybe<A> = Nothing | Just<A>;
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Maybe: Maybe<A>;
}
}
export const maybe: Applicative1<'Maybe'> & Traversable1<'Maybe'> = {
URI: 'Maybe',
map: f => fa => (fa._tag === 'Just' ? just(f(fa.value)) : nothing),
ap: fab => fa => (fab._tag === 'Nothing' ? nothing : fa._tag === 'Nothing' ? nothing : just(fab.value(fa.value))),
of: just,
traverse: <F>(
F: Applicative<F>,
): (<A, B>(f: (a: A) => HKT<F, B>) => (ta: Maybe<A>) => HKT<F, Maybe<B>>) => f => ta =>
ta._tag === 'Nothing'
? F.of(nothing)
: pipe(
ta.value,
f,
F.map(just),
),
};
////////
export type Box<A> = [A];
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Box: Box<A>;
}
}
export const box: Applicative1<'Box'> & Traversable1<'Box'> = {
URI: 'Box',
map: f => fa => [f(fa[0])],
ap: fab => fa => [fab[0](fa[0])],
of: a => [a],
traverse: <F>(F: Applicative<F>): (<A, B>(f: (a: A) => HKT<F, B>) => (ta: Box<A>) => HKT<F, Box<B>>) => f => ta =>
pipe(
ta[0],
f,
F.map(v => [v]),
),
};
//////////
const test: Box<Maybe<number>> = pipe(
just(123),
maybe.traverse(box)(n => [n * 2]),
);
const test2: Maybe<Box<number>> = pipe(
[123],
box.traverse(maybe)(n => just(n * 2)),
); |
@gcanti Why closing? Do you have any considerations about moving to data-last at least in the next major? Registering any monad transformer or composition factory output in HKT registry only to fit pipeable (which is also incapable of adding any extra helpers outside the lib core) is terrible. And what about pipeable traversable? |
tagged as a reminder |
I looked in both documentation and issues, but couldn't find any information on this...
Is it possible (right now or planned as a feature) to make composition "pipe-friendly" with a simple syntax?
Currently *Composition signatures cannot be nicely piped without introducing a function wrapper. For example in case of a OptionT monad transformer below:
The text was updated successfully, but these errors were encountered: