-
-
Save dypsilon/883e878ca1c05a7c355e41fb28a2f3e3 to your computer and use it in GitHub Desktop.
| /** | |
| * This short program will encrypt the user password | |
| * and insert a new record into a mock database. | |
| */ | |
| const Reader = require('fantasy-readers'); | |
| const R = require('ramda'); | |
| const crypto = require('crypto'); | |
| // 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) => db.push(doc), | |
| get: (i) => db[i], | |
| delete: (i) => db.splice(i, 1), | |
| list: () => db | |
| } | |
| } | |
| // some utility functions | |
| const encrypt = (i) => crypto.createHash('sha1').update(i).digest('hex'); | |
| const encPassword = R.evolve({password: encrypt}) | |
| const getInput = () => ({ email: '[email protected]', password: 'secret' }); | |
| // this is how you access the db connection inside the reader | |
| const save = (user) => { | |
| return Reader.ask.map((db) => { | |
| db.insert(user); | |
| return db.list(); | |
| }); | |
| } | |
| // the body of the program | |
| const prog = R.pipe( | |
| Reader.of, | |
| R.map(encPassword), | |
| R.chain(save) | |
| ); | |
| // this is our db connection now | |
| const dbCon = connectTo(database); | |
| // this is how you pass the db connection in | |
| const result = prog(getInput()).run(dbCon); | |
| // show the output | |
| console.log(result); |
This is nice, but quite simplistic. I would love to see an implementation involving other monads for async operations (which DB interactions usually are)
@danielo515 I ran into the same issue when I was trying to use a Reader Monad for dependency injection. I was having to do a bunch of nested 'map's to get to the value inside the promise. But then I learned about monad transformers! I wrote one called ReaderPromise which seems to do the trick. https://github.com/danny-andrews/circleci-weigh-in/blob/master/src/shared/reader-promise.js
used this technique in a big refactor and it is great!
Very very nice!
I'm trying to understand this monad, have low understanding of FP. Why can't we just curry the save function? What am I missing?
// this is how you access the db connection inside the reader
const save = user => db => {
db.insert(user);
return db.list();
};
// the body of the program
const prog = R.pipe(encPassword, save);
// this is our db connection now
const dbCon = connectTo(database);
// this is how you pass the db connection in
const result = prog(getInput())(dbCon);UPD:
Got it. Seems in this particular case there is no difference. But in case we want manipulate with result of saving, then Reader monad works like a charm.
const prog = R.pipe(
Reader.of,
R.map(encPassword),
R.chain(save),
R.map(console.log), // <-- here is the point
);This is nice, but quite simplistic. I would love to see an implementation involving other monads for async operations (which DB interactions usually are)
You can check the state monad implementation by
https://github.com/dicearr/monastic
Exploit it as as a reader monad like
const { get: ask } = require ('monastic')
...and for async computation, combine StateT with F.Future from
https://github.com/fluture-js/Fluture
Beautiful