Skip to content

Instantly share code, notes, and snippets.

@dypsilon
Last active December 27, 2017 03:53
Show Gist options
  • Save dypsilon/8acae3960e9f4a36a295caca2084b7fe to your computer and use it in GitHub Desktop.
Save dypsilon/8acae3960e9f4a36a295caca2084b7fe to your computer and use it in GitHub Desktop.
In this example, the save method produces an error and we use mapLower to call a special Future function "mapRej", which transforms all incoming errors.
const {tagged} = require('daggy');
// const
const K = (a) => (b) => a;
// Reader Transformer
module.exports = (M) => {
const ReaderT = tagged('run');
ReaderT.lift = ReaderT.prototype.lift = (m) => ReaderT(K(m));
ReaderT.of = ReaderT.prototype.of = (a) => ReaderT((e) => M.of(a));
ReaderT.ask = ReaderT.prototype.ask = ReaderT((e) => M.of(e));
ReaderT.prototype.chain = function(f) {
return ReaderT((e) => {
return this.run(e).chain((a) => f(a).run(e));
});
};
ReaderT.prototype.map = function(f) {
var m = this;
return m.chain(a => m.of(f(a)))
}
ReaderT.prototype.mapLower = function(f) {
return ReaderT(a => f(this.run(a)));
}
return ReaderT;
};
/**
* This short program will encrypt the user password
* and insert a new record into a mock database.
*/
const ReaderT = require('./lib/reader-t');
const Future = require('fluture');
const R = require('ramda');
const crypto = require('crypto');
const FutureReader = ReaderT(Future);
// our mock database
const database = [
{ email: '[email protected]', password: 'e0538fd8f022bb3b139d72cf12766cb0e31690ff' },
{ email: '[email protected]', password: '42c4fbf6fec201c66b82c97833b08d936d2cd526' }
]
// creates a statefull database connection
const connectTo = (db) => {
return {
insert: (doc) => Future.of(db.push(doc)),
get: (i) => Future.of(db[i]),
delete: (i) => Future.of(db.splice(i, 1)),
list: () => Future.of(db)
}
}
// some utility functions
const encrypt = (i) => crypto.createHash('sha1').update(i).digest('hex');
const encPassword = R.evolve({password: encrypt})
// utility function which makes it easier
// to ask for some dependency
// first parameter is the selector function which
// is applied to the environment to get a specific
// dependency (inject(R.identity, f, reader) returns the whole
// environment)
const inject = R.curry((sel, f, reader) => {
return reader.chain((v) => {
return reader.ask.chain((env) => {
const dep = sel(env);
return reader.lift(f(dep, v));
});
});
});
// utility function which invokes mapLower on the object
const mapLower = R.invoker(1, 'mapLower');
// this is how you access the db connection inside the reader
const save = (db, user) => {
//return db.insert(user).chain(_ => db.list());
return Future.reject('Some error just happened.');
}
// this utility function is designed to work inside
// mapLower call and convert all incoming errors to uppercase
// it is using the special mapRej function of Future
const shoutErrors = Future.mapRej(R.toUpper);
// the body of the program
const handler = R.pipe(
R.map(encPassword),
inject(R.prop('db'), save),
R.map(R.map(R.dissoc('password'))),
mapLower(shoutErrors)
);
// this is our db connection now
const dbCon = connectTo(database);
const request = { email: '[email protected]', password: 'secret' };
const result = handler(FutureReader.of(request))
.run({ 'db': dbCon }) // this is how you pass the db connection in
.fork(console.error, console.log); // now fork the future
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment