Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ecancino/792e09a7caf3f84656161448058c1cff to your computer and use it in GitHub Desktop.

Select an option

Save ecancino/792e09a7caf3f84656161448058c1cff to your computer and use it in GitHub Desktop.
Egghead Composable Javascript Spotify
const Task = require('data.task')
const Either = require('data.either')
const { Left, Right } = Either
const request = require('request')
const { List } = require('immutable-ext')
const { drop, prop, map } = require('lodash/fp')
const effect = f => x => {
f(x)
return x
}
// eitherToTask :: Either a b -> Task a b
const eitherToTask = e => e.fold(Task.rejected, Task.of)
// taskToEither :: Task a b -> Either a b
const taskToEither = t => t.fold(Left, Right)
// argv :: Task _ [String]
const argv = new Task((rej, res) => res(process.argv)).map(drop(2))
// get :: String -> Task Error String
const get = url => new Task((rej, res) => {
request(url, (error, response, body) => {
if (!error && response.statusCode == 200) res(body)
else rej(error)
})
})
// first :: List a -> Either Error a
const first = xs =>
xs[0]
? Right(xs[0])
: Left(new Error('array is empty'))
// intersect :: List a -> List a -> List a
const intersect = (xs, ys) =>
xs.filter(x => ys.some(y => x === y))
// intersect :: List (List a) -> Either (List a)
const intersectAll = xLists =>
xLists.length
? Right(xLists.reduce(intersect))
: Left(new Error('no arrays to intersect'))
// parse :: String -> Either Error Json
const parse = json =>
Either.try(JSON.parse)(json)
// Id :: Name :: String
// searchArtist :: String -> Task Error Artist
const searchArtist = q =>
get(`https://api.spotify.com/v1/search?q=${encodeURI(q)}&type=artist`)
.map(parse)
.chain(eitherToTask)
.map(res => res.artists.items)
.map(first)
.chain(eitherToTask)
// relatedArtists :: Id -> Task Error (List Artist)
const relatedArtists = id =>
get(`https://api.spotify.com/v1/artists/${id}/related-artists`)
.map(parse)
.chain(eitherToTask)
.map(res => List(res.artists))
// [String] -> Task Error [Name]
const main = names =>
// List String
List(names)
// Task Error (List Artist)
.traverse(Task.of, name => searchArtist(name))
// Task Error (List Id)
.map(artists => artists.map(prop('id')))
// Task Error (List (List Artist))
.chain(ids => ids.traverse(Task.of, relatedArtists))
// Task Error (List (List Name))
.map(relLists => relLists.map(rels => rels.map(prop('name'))))
// Task Error Either (List Name)
.map(intersectAll)
// Task Error (List Name)
.chain(eitherToTask)
// Task Error [Name]
.map(relatedArtistNames => relatedArtistNames.toJSON())
argv
.chain(main)
.fork(console.error, console.log)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment