const util = require("util")
const { Obj, Arr, Fn, implement, Functor, Apply } = require("@masaeedu/fp")
const { adt } = require("@masaeedu/adt")
// :: type Apply f = { lift2: (a -> b -> c) -> f a -> f b -> f c }
// :: type Applicative f = { of: a -> f a } & (Apply f)
// :: type Either l r = { label: "Left", values: '[l] } | { label: "Right", values: '[r] }
// :: {
// :: Left: a -> Either a b,
// :: Right: b -> Either a b,
// :: match: { Left: a -> c, Right: b -> c } -> Either a b -> c
// :: }
const Either = adt({ Left: ["a"], Right: ["b"] })
const { Left, Right, match } = Either
// :: type MaybeApply f a = Either (f a) a
// :: Apply f -> Applicative (MaybeApply f)
const MaybeApply = A => {
const of = Right
const lift2 = f => match({
Left: ax => match({
Left: ay => Left(A.lift2(f)(ax)(ay)),
Right: y => Left(A.map(x => f(x)(y))(ax))
}),
Right: x => match({
Left: ay => Left(A.map(y => f(x)(y))(ay)),
Right: y => Right(f(x)(y))
})
})
return Fn.flip(Fn.pipe)({ of, lift2 })([
implement(Apply),
implement(Functor),
implement(Apply)
])
}
// Obj only implements Apply, not Applicative, so for example Obj.of is undefined
console.log(Obj.of)
// However, MaybeApply(Obj) is an applicative
console.log(MaybeApply(Obj).of)
// This means we can sequence arrays of objects by simply promoting them
// :: (StrMap String)[]
const input = [{ foo: "hello", bar: "hello" }, { foo: "world", bar: "world" }]
// :: Applicative (MaybeApply StrMap)
const A = MaybeApply(Obj)
// :: Either (StrMap String[]) String[]
const result = Arr.traverse(A)(Left)(input)
const log = x => console.log(util.inspect(x, { depth: null }))
log(match({
Left: x => x,
Right: x => x
})(result))
// { foo: [ 'hello', 'world' ], bar: [ 'hello', 'world' ] }