I have Array<Validation<A, B>> and want to aggregate all the successes and failures, e.g. Array<Validation<A, B>> => Validation<Array<A>, Array<B>>.
Do you have any ideas how I would do this?
Context: I have a bunch of async tasks that happen in parallel, and if one or more fail, I want all the failures to display to the user鈥攐therwise I want all the successes.
Thank to your question I found some defects in the typings of the Validation module, just released v0.2.4).
You are looking for a function with the following signature
Array<Validation<A, B>> -> Validation<A, Array<B>>
where A is a semigroup. That's sequence (module Traversable). Note that the return type is Validation<A, Array<B>> (not Validation<Array<A>, Array<B>>) because the type A, having a semigroup instance, can be accumulated.
import * as array from 'fp-ts/lib/Array'
import * as validation from 'fp-ts/lib/Validation'
import { monoidString } from 'fp-ts/lib/Monoid'
import { sequence } from 'fp-ts/lib/Traversable'
const success = [
validation.success<string, number>(1),
validation.success<string, number>(2),
validation.success<string, number>(3)
]
// here I'm using a monoid on strings but you can use any semigroup
const failure = [
validation.success<string, number>(1),
validation.failure<string, number>(monoidString, '[fail 1]'),
validation.failure<string, number>(monoidString, '[fail 2]')
]
const x = sequence(validation, array)(success)
console.log(x) // => Success([1,2,3])
const y = sequence(validation, array)(failure)
console.log(y) // => Failure("[fail 1][fail 2]")
Context: I have a bunch of async tasks that happen in parallel, and if one or more fail, I want all the failures to display to the user鈥攐therwise I want all the successes
If Task is involved, the solution is more complex. Fortunately applicatives (in this case task and validation) are composable
import { StaticApplicative } from 'fp-ts/lib/Applicative'
const URI = 'TaskValidation'
type URI = typeof URI
export type TaskValidation<L, A> = task.Task<validation.Validation<L, A>>
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
TaskValidation: TaskValidation<any, A>
}
interface HKT2<A, B> {
TaskValidation: TaskValidation<A, B>
}
}
function map<L, A, B>(f: (a: A) => B, fa: TaskValidation<L, A>): TaskValidation<L, B> {
return fa.map(v => v.map(f))
}
function of<L, A>(a: A): TaskValidation<L, A> {
return task.of(validation.of<L, A>(a))
}
function ap<L, A, B>(fab: TaskValidation<L, (a: A) => B>, fa: TaskValidation<L, A>): TaskValidation<L, B> {
return fa.ap(fab.map(v => (x: validation.Validation<L, A>) => x.ap(v)))
}
// composition of task and validation
const taskValidationApplicative: StaticApplicative<URI> = {
URI,
map,
of,
ap
}
const asyncSuccess = success.map(task.of)
const asyncFailure = failure.map(task.of)
const asyncX = sequence(taskValidationApplicative, array)(asyncSuccess)
asyncX.run().then(x => console.log(x)) // => Success([1,2,3])
const asyncY = sequence(taskValidationApplicative, array)(asyncFailure)
asyncY.run().then(x => console.log(x)) // => Failure("[fail 1][fail 2]")
Composing applicatives by hand like this is tedious, it would be nice to figure out how to compose them once for all.
Fortunately applicatives are composable
@OliverJAsh by the way I just realized that this could be a better answer than monad transformers to your issue "Using Task with Either or Validation" https://github.com/gcanti/fp-ts/issues/35
(note: actually functors, applicatives, foldables and traversables are composable)
Great, thanks for the answer. I thought sequence only worked on monads but I was wrong鈥攊t works on applicatives!
Going along the lines of your taskValidationApplicative example, is there such a thing as an "applicative transformer", like monad transformers?
is there such a thing as an "applicative transformer"
It's just "composition". Here's the (static-land) composition of two generic applicatives
import { HKT, HKTS } from 'fp-ts/lib/HKT'
import { StaticFunctor, FantasyFunctor } from 'fp-ts/lib/Functor'
import { StaticApplicative, FantasyApplicative } from 'fp-ts/lib/Applicative'
/** returns the composition of two functors */
export function getStaticFunctor<FG extends HKTS>(URI: FG): <F extends HKTS, G extends HKTS>(functorF: StaticFunctor<F>, functorG: StaticFunctor<G>) => StaticFunctor<FG> {
return <F extends HKTS, G extends HKTS>(functorF: StaticFunctor<F>, functorG: StaticFunctor<G>) => ({
URI,
map<A, B>(f: (a: A) => B, fa: HKT<HKT<A>[G]>[F]): HKT<HKT<B>[G]>[F] {
return functorF.map((ga: HKT<A>[G]) => functorG.map(f, ga), fa)
}
})
}
/** returns the composition of two applicatives */
export function getStaticApplicative<FG extends HKTS>(URI: FG): <F extends HKTS, G extends HKTS>(applicativeF: StaticApplicative<F>, applicativeG: StaticApplicative<G>) => StaticApplicative<FG> {
return <F extends HKTS, G extends HKTS>(applicativeF: StaticApplicative<F>, applicativeG: StaticApplicative<G>) => {
const functor = getStaticFunctor(URI)(applicativeF, applicativeG)
function of<A>(a: A): HKT<HKT<A>[G]>[F] {
return applicativeF.of(applicativeG.of(a))
}
function ap<A, B>(fgab: HKT<HKT<(a: A) => B>[G]>[F], fga: HKT<HKT<A>[G]>[F]): HKT<HKT<B>[G]>[F] {
return applicativeF.ap<HKT<A>[G], HKT<B>[G]>(applicativeF.map((h: HKT<(a: A) => B>[G]) => (ga: HKT<A>[G]) => applicativeG.ap<A, B>(h, ga), fgab), fga)
}
return {
...functor,
of,
ap
}
}
}
//
// Usage
//
import * as validation from 'fp-ts/lib/Validation'
import * as task from 'fp-ts/lib/Task'
import { monoidString } from 'fp-ts/lib/Monoid'
import { sequence } from 'fp-ts/lib/Traversable'
import * as array from 'fp-ts/lib/Array'
export const TaskValidationURI = 'TaskValidation'
export type TaskValidationURI = typeof TaskValidationURI
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
TaskValidation: task.Task<validation.Validation<any, A>>
}
interface HKT2<A, B> {
TaskValidation: task.Task<validation.Validation<A, B>>
}
}
const taskValidationApplicative = getStaticApplicative(TaskValidationURI)(task, validation)
const allsuccess = [
validation.success<string, number>(1),
validation.success<string, number>(2),
validation.success<string, number>(3)
].map(task.of)
const somefailure = [
validation.success<string, number>(1),
validation.failure<string, number>(monoidString, '[fail 1]'),
validation.failure<string, number>(monoidString, '[fail 2]')
].map(task.of)
const asyncX = sequence(taskValidationApplicative, array)(allsuccess)
asyncX.run().then(x => console.log(x)) // => Success([1,2,3])
const asyncY = sequence(taskValidationApplicative, array)(somefailure)
asyncY.run().then(x => console.log(x)) // => Failure("[fail 1][fail 2]")
Thanks for adding this to the library!
Is there any way to take an existing Task<Validation<L, A>> and convert it into TaskValidation<L, A>?
There's no need to convert anything if working with static dictionaries is fine for you. getApplicativeComposition returns a StaticApplicative, that is a dictionary
{ URI, map, of, ap }
that you can use to map, of, ap your Task<Validation<L, A>> raw instances. Indeed the module augmentation in the comment above is
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
TaskValidation: task.Task<validation.Validation<any, A>> // <= "raw" types
}
interface HKT2<A, B> {
TaskValidation: task.Task<validation.Validation<A, B>>
}
}
However, if you find working with static dictionaries awkward, you can define a wrapper for Task<Validation<L, A>> called for example TaskValidation<L, A> and modify the module augmentation accordingly
// module TaskValidation.ts
import * as validation from 'fp-ts/lib/Validation'
import * as task from 'fp-ts/lib/Task'
import { getApplicativeComposition, FantasyApplicative } from 'fp-ts/lib/Applicative'
export const URI = 'TaskValidation'
export type URI = typeof URI
const taskValidationApplicative = getApplicativeComposition('Task<Validation>')(task, validation)
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
'Task<Validation>': task.Task<validation.Validation<any, A>>
TaskValidation: TaskValidation<any, A>
}
interface HKT2<A, B> {
'Task<Validation>': task.Task<validation.Validation<A, B>>
TaskValidation: TaskValidation<A, B>
}
}
export class TaskValidation<L, A> implements FantasyApplicative<URI, A> {
static of = of
readonly _L: L
readonly _A: A
readonly _URI: URI
constructor(public readonly value: task.Task<validation.Validation<L, A>>) {}
map<B>(f: (a: A) => B): TaskValidation<L, B> {
return new TaskValidation(taskValidationApplicative.map(f, this.value))
}
of<B>(b: B): TaskValidation<L, B> {
return of<L, B>(b)
}
ap<B>(fab: TaskValidation<L, (a: A) => B>): TaskValidation<L, B> {
return new TaskValidation(taskValidationApplicative.ap(fab.value, this.value))
}
}
export function map<L, A, B>(f: (a: A) => B, fa: TaskValidation<L, A>): TaskValidation<L, B> {
return fa.map(f)
}
export function of<L, A>(a: A): TaskValidation<L, A> {
return new TaskValidation(taskValidationApplicative.of(a))
}
export function ap<L, A, B>(fab: TaskValidation<L, (a: A) => B>, fa: TaskValidation<L, A>): TaskValidation<L, B> {
return fa.ap(fab)
}
That makes sense, thanks @gcanti. Hope you don't mind me having all these questions. This is the best library I've found for learning/using FP in TypeScript, and I'm immensely thankful for your support.
I tried using your TaskValidation definition, but got these compile errors:
src/TaskValidation.ts(17,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'TaskValidation' must be of type 'Task<Validation<any, A>>', but here has type 'TaskValidation<any, A>'.
src/TaskValidation.ts(21,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'TaskValidation' must be of type 'Task<Validation<A, B>>', but here has type 'TaskValidation<A, B>'.
src/TaskValidation.ts(25,14): error TS2420: Class 'TaskValidation<L, A>' incorrectly implements interface 'FantasyApplicative<"TaskValidation", A>'.
Types of property 'of' are incompatible.
Type '<B>(b: B) => TaskValidation<L, B>' is not assignable to type '<A>(a: A) => Task<Validation<any, A>>'.
Type 'TaskValidation<L, any>' is not assignable to type 'Task<Validation<any, any>>'.
Types of property 'value' are incompatible.
Type 'Task<Validation<L, any>>' is not assignable to type 'Lazy<Promise<Validation<any, any>>>'.
Type 'Task<Validation<L, any>>' provides no match for the signature '(): Promise<Validation<any, any>>'.
My lack of understanding for higher kinded types is making it hard for me to debug this myself.
Could you post the source of TaskValidation.ts?
It's actually verbatim the source code you posted, using fp-ts v.0.2.7 Does it error for you?
Ah ok, I guess you have somewhere a duplicated module augmentation for the key TaskValidation, something like
// file A
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
'Task<Validation>': task.Task<validation.Validation<any, A>>
TaskValidation: TaskValidation<any, A>
}
interface HKT2<A, B> {
'Task<Validation>': task.Task<validation.Validation<A, B>>
TaskValidation: TaskValidation<A, B>
}
}
// file B
declare module 'fp-ts/lib/HKT' {
interface HKT<A> {
TaskValidation: task.Task<validation.Validation<any, A>>
}
interface HKT2<A, B> {
TaskValidation: task.Task<validation.Validation<A, B>>
}
}
Ah, thanks. Sorry for wasting your time on that!!
Hope you don't mind me having all these questions
Actually I'm glad you have all these questions: I guess this library needs improvements derived from real world usages. Keep in mind that faking higher kinded types is overall still an unexplored land: fp-ts is highly experimental, sometimes I feel like I'm pushing TypeScript to its limits and I'm a bit worried about making some crucial mistakes ;)
What is the recommended way to implement the following?
type Validator<T> = (value: T) => Validation<string[], T>
const validateAll = <T>
(validators: Validator<T>[]) =>
(value: T): Validation<string[], T> =>
// how to implement?
@christianbradley using sequence (or traverse)
import { Validation, getApplicative } from 'fp-ts/lib/Validation'
import { sequence } from 'fp-ts/lib/Traversable'
import { array } from 'fp-ts/lib/Array'
import { getArraySemigroup } from 'fp-ts/lib/Semigroup'
const A = getApplicative(getArraySemigroup<string>())
const sequenceValidations = sequence(A, array)
type Validator<T> = (value: T) => Validation<string[], T>
const validateAll = <T>(validators: Validator<T>[]) => (value: T): Validation<string[], T> => {
return sequenceValidations(validators.map(validator => validator(value))).map(() => value)
}
using sequence (or traverse)
@gcanti Please add such useful examples to the repo! :)
When I tried an example code from the buginnig of current issue (https://github.com/gcanti/fp-ts/issues/50#issuecomment-298296346) to execute it fails with:
error TS2345: Argument of type 'typeof import("/home/vasyl/dev/fp-ts-test/node_modules/fp-ts/lib/Validation")' is not assignable to parameter of type 'Applicative<{}>'.
Property 'of' is missing in type 'typeof import("/home/vasyl/dev/fp-ts-test/node_modules/fp-ts/lib/Validation")'.
Is there anyone who can suggest me what am I doing wrong, maybe there is some regression? I just copy-pasted a code sample, all other examples that I tried worked perfectly well...
@Vasylll the comment you are referring to is pretty old, here's the same example but compatible with the current stable version (1.12.0)
import { array } from 'fp-ts/lib/Array'
import { monoidString } from 'fp-ts/lib/Monoid'
import { failure, getApplicative, success } from 'fp-ts/lib/Validation'
const successes = [success<string, number>(1), success<string, number>(2), success<string, number>(3)]
const failures = [success<string, number>(1), failure<string, number>('[fail 1]'), failure<string, number>('[fail 2]')]
const validationApplicative = getApplicative(monoidString)
const x = array.sequence(validationApplicative)(successes)
console.log(x) // => Success([1,2,3])
const y = array.sequence(validationApplicative)(failures)
console.log(y) // => Failure("[fail 1][fail 2]")
@gcanti, thanks a lot
Most helpful comment
@christianbradley using
sequence(ortraverse)