Skip to content

Instantly share code, notes, and snippets.

@kutyel
Created October 31, 2017 10:47
Show Gist options
  • Save kutyel/9b6db1deea7f373952ac3cfb6915e66f to your computer and use it in GitHub Desktop.
Save kutyel/9b6db1deea7f373952ac3cfb6915e66f to your computer and use it in GitHub Desktop.
Professor Frisby's functional programming exercises (Part 4: monads)
require('./support')
const path = require('path')
const expect = require('expect')
const Task = require('data.task')
const {
chain,
curry,
compose,
identity,
prop,
split,
last,
map,
} = require('ramda')
// Exercise 1
// ==========
// Use safeProp and map/join or chain to safely get the street name when given
// a user.
const safeProp = curry((x, o) => Maybe.of(o[x]))
const user = {
id: 2,
name: 'albert',
address: {
street: {
number: 22,
name: 'Walnut St',
},
},
}
const ex1 = compose(
chain(safeProp('name')),
chain(safeProp('street')),
safeProp('address')
)
expect(ex1(user)).toEqual(Maybe.of('Walnut St'))
// Exercise 2
// ==========
// Use getFile to get the filename, remove the directory so it's just the file,
// then purely log it.
const getFile = () => new IO(() => __filename)
const pureLog = x => new IO(() => (console.log(x), `logged ${x}`))
const ex2 = compose(chain(compose(pureLog, last, split(path.sep))), getFile)
expect(ex2().unsafePerformIO()).toBe('logged monad.js')
// Exercise 3
// ==========
// Use getPost() then pass the post's id to getComments().
//
const getPost = i =>
new Task((rej, res) =>
setTimeout(() => res({ id: i, title: 'Love them tasks' }), 300)
)
const getComments = i =>
new Task((rej, res) =>
setTimeout(
() =>
res([
{ post_id: i, body: 'This book should be illegal' },
{ post_id: i, body: 'Monads are like smelly shallots' },
]),
300
)
)
const ex3 = compose(chain(compose(getComments, prop('id'))), getPost)
ex3(13).fork(console.log, res => {
expect(res.map(prop('post_id'))).toEqual([13, 13])
expect(res.map(prop('body'))).toEqual([
'This book should be illegal',
'Monads are like smelly shallots',
])
})
// Exercise 4
// ==========
// Use validateEmail, addToMailingList, and emailBlast to implement ex4's type
// signature.
// addToMailingList :: Email -> IO([Email])
const addToMailingList = (list => email =>
new IO(() => (list.push(email), list)))([])
const emailBlast = list => new IO(() => `emailed: ${list.join(',')}`)
const validateEmail = x =>
x.match(/\S+@\S+\.\S+/) ? new Right(x) : new Left('invalid email')
// ex4 :: Email -> Either String (IO String)
const ex4 = compose(map(compose(emailBlast, addToMailingList)), validateEmail)
const getResult = either(identity, unsafePerformIO)
expect(getResult(ex4('notanemail'))).toBe('invalid email')
expect(getResult(ex4('[email protected]'))).toBe(
'emailed: [email protected]'
)
console.info('You are now a MONAD genius!!! πŸ€“ πŸ‘πŸΌ πŸ‘πŸΌ πŸ‘πŸΌ')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment