Package Exports
- inquiry-monad
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (inquiry-monad) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Inquiry Monad
v0.16.5
Experiment with aggregate Left/Right monad running parallel. More details when it is better fleshed out.
Note: From 0.15+ Futures are now supported through inquiry-monad-futures
Basic example
const {Inquiry, InquiryP, Pass, Fail} = require('inquiry-monad');
const subjectData = {
a: 1,
b: false
};
const hasA = x => x.a ? Pass('has a') : Fail('does not have a');
const validateB = x => x.b && typeof x.b === 'boolean' ? Pass('b is valid') : Fail('b is invalid');
const hasNoC = x => x.c ? Fail('has a c value') : Pass('has no c value');
/* With all passes */
Inquiry.subject(subjectData)
.informant(console.log) // observation function: this will output console.logs of all future `inquire` results
.inquire(hasA)
.inquire(validateB)
.inquire(hasNoC)
.join();
// >> result: {subject: {a:1, b:false}, pass: Pass(['has a', 'b is valid', 'has no c value']), fail: Fail([]), iou: IOU([])}
/* With failures */
const subjectDataWithFailure = {
a: 1,
b: 'string',
c: true
};
Inquiry.subject(subjectDataWithFailure)
.informant(console.log)
.inquire(hasA)
.inquire(validateB)
.inquire(hasNoC)
.join();
// >> result: {subject: {a:1, b:'string', c:true}, pass: Pass(['has a']), fail: Fail(['b is invalid', 'has c value']), iou: IOU()}
/* With async Promises */
const checkDb = async (x) => Promise.resolve(Pass('pretend I looked something up in a db'));
InquiryP.subject(subjectDataWithFailure)
.informant(console.log)
.inquire(checkDb)
.inquire(hasA)
.inquire(validateB)
.inquire(hasNoC)
.conclude(x => x, y => y);
// for now .conclude or another unwrap fn is necessary to complete "IOUs" to give a clean exit (unresolved Promises)
// >> Promise.resolve(result: {subject: {a:1, b:'string', c:true}, pass: Pass(['has a', 'pretend I looked something up in a db']), fail: Fail(['b is invalid', 'has c value']), iou: IOU()})
Description
Inquiry can take any value (a subject), store it within an immutable container (Inquiry
or InquiryP
monad) to be tested against various functions (via .inquire
method) and resulting in two or three lists: Pass([])
, Fail([])
, and sometimes IOU([])
in the case of InquiryP
.
The advantage over traditional Promise chains is that the original subject and each result is retained through the resulting chain of functions, giving complete observability over the data passed through. Also, this allows one to restrain Promises to stay with a monadic structure, bolstering immutibility.
For those who wish to compare to a traditional Left/Right in functional programming, there are many advantages over a Left/Right or Validation pattern from functional:
- Inquiry aggregates all results, not just failures.
- Inquiry can run functions against both sides (traditional Left/Right as Fail/Pass)
- Inquiry retains the original subject rather than transforming it into a result
- Inquiry is designed to be an expressive, easily understood API
Constructor
Inquiry.subject(value)
or InquiryP.subject(value)
(Promise/async-based)
Returns an Inquiry
monad, which is monad containing an object with properties subject
, pass
, fail
, iou
, and an informant
method.
subject
: contains value
passed to Inquiry.subject
within a Maybe
monad, meaning it will either be Just(value)
or Nothing()
.
pass
: contains a Pass
monad containing an array of values
fail
: contains a Fail
monad containing an array of values
iou
: contains an IOU
monad contains an array of Promises (only relevant with InquiryP
)
informant
: contains a function to be called upon the return of a .inquire
call, for observation or logging purposes (set by calling .informant
method)
Using the above object structure, you may also assemble your own Inquiry
monad "manually" with Inquiry.of
, those this is generally unnecessary.
As an basic example:
const value = {something: true};
console.log(Inquiry.subject(value).informant(console.log).join()); // .join will reveal contained value
// > {subject: Just({something: true}), pass: Pass([]), fail: Fail([]), iou: IOU([]), informant: console.log};
Methods
Core method
.inquire(f)
: give inquire a function f
that returns either a Pass()
, Fail()
, Promise (InquiryP
only), or another Inquiry
. Anything other than these will be assumed as a Pass
.
const isMoreThanOne = x => x > 1 ? Pass('Is greater than 1') : Fail('Is less than or equal to 1');
Inquiry.subject(5)
.inquire(isMoreThanOne)
.join();
// > {subject: Just(5), pass: Pass(['Is greater than 1']), fail: Fail([]), iou: IOU([]), informant: _ => _};
Interrogative methods:
.inspect()
: return a string with the value contained in the Inquiry monad. Used for debugging.
const isMoreThanOne = x => x > 1 ? Pass('Is greater than 1') : Fail('Is less than or equal to 1');
Inquiry.subject(5)
.inquire(isMoreThanOne)
.inspect(); // outputs to string
// > Inquiry({subject: Just(5), pass: Pass(['Is greater than 1']), fail: Fail([]), iou: IOU([])});
.informant(f)
: call function f
upon each .inquire
result. Useful for logging or observing. The function will be passed an array
containing ['fnName', Pass('passed value')]
or ['fnName', Fail('failed value')]
. Is not run when IOU is added, however does run once the IOU resolves.
const isMoreThanOne = x => x > 1 ? Pass('Is greater than 1') : Fail('Is less than or equal to 1');
const isMoreThanTen = x => x > 10 ? Pass('Is greater than 10') : Fail('Is less than or equal to 10');
Inquiry.subject(5)
.informant(console.log)
.inquire(isMoreThanOne)
.inquire(isMoreThanTen);
// console.log would output:
// > 'isMoreThanOne', Pass('Is greater than 1')
// > 'isMoreThanTen', Fail('Is less than or equal to 10')
Unwrap methods:
.conclude(f, g)
: returns the full Inquiry
's value, with map functions applied to both fail (f
) and pass (g
), but will wait to resolve all outstanding IOUs (Promises)
const isMoreThanOne = x => x > 1 ? Pass({greaterThanOne: true}) : Fail({greaterThanOne: false});
const isMoreThanTen = x => x > 10 ? Pass({greaterThanTen: true}) : Fail({greaterThanTen: false});
Inquiry.subject(5)
.inquire(isMoreThanOne)
.inquire(isMoreThanTen)
.conclude(
x => ({
failCount: x.join().length,
fails: x.join()
}),
y => ({
passCount: y.join().length,
passes: y.join()
})
);
// > {subject: Just(5), pass: {passCount: 1, passes: ['Is greater than 1']}, fail: {failCount: 1, fails: ['Is less than or equal to 10']}, iou: IOU([]), informant: _ => _};
.fork(f, g)
: exit and either run a function f
if there are any fails, or g
if no fails, returning only result of the function executed.
const isMoreThanOne = x => x > 1 ? Pass({greaterThanOne: true}) : Fail({greaterThanOne: false});
const isMoreThanTen = x => x > 10 ? Pass({greaterThanTen: true}) : Fail({greaterThanTen: false});
Inquiry.subject(5)
.inquire(isMoreThanOne)
.inquire(isMoreThanTen)
.fork(
x => ({
failCount: x.join().length,
fails: x.join()
}),
y => ({
passCount: y.join().length,
passes: y.join()
})
);
// > {failCount: 1, fails: ['Is less than or equal to 10']}
Inquiry.subject(15)
.inquire(isMoreThanOne)
.inquire(isMoreThanTen)
.fork(
x => ({
failCount: x.join().length,
fails: x.join()
}),
y => ({
passCount: y.join().length,
passes: y.join()
})
);
// > {passCount: 2, passes: ['Is greater than 1', 'Is greater than 10']}
.zip(f)
: run function f
against a merged list of pass
and fail
// example forthcoming
Early results methods:
.breakpoint(f)
: run a function f
only if fail
has contents. f
must return an Inquiry
.
.milestone(f)
: run a function f
only if pass
has contents. f
must return an Inquiry
.
.await()
(InquiryP
only): pause and wait for all iou
Promises to resolve.
Multi-map method:
.unison(f)
: run a function f
against both pass
and fail
branches
// example forthcoming
Flow-control method:
.swap()
: swap the pass
and fail
branches
// example forthcoming
Standard monadic methods:
Documentation forthcoming for the following.
ap
map
chain
join