This repository has been archived by the owner on Feb 18, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
'use strict'; | ||
|
||
const fs = require('fs'); | ||
|
||
const exit0 = require('./common/exit0'); | ||
const exit1 = require('./common/exit1'); | ||
const { | ||
Arr, | ||
Fn, | ||
Str, | ||
Path, | ||
NodeEither, | ||
GenericEitherT, | ||
Cont, | ||
} = require('./common/revengeutils'); | ||
|
||
const getpath = Path.combine(process.argv[2]); | ||
|
||
// A monad for working with continuations that contain NodeEithers | ||
// :: type NC r e x = GenericEither (Cont r) NodeEither e x | ||
// :: = Cont r (NodeEither e x) | ||
const NC = GenericEitherT(NodeEither)(Cont); | ||
|
||
// Standard NodeJS APIs need a little massaging to be valid continuations | ||
// - Callback must be a curried, final argument | ||
// - Can only pass a single argument to callback (an [err, ...data] array is fine) | ||
// :: Path -> NC () String String | ||
const readFile = path => cb => fs.readFile(path, 'utf8', (...args) => cb(args)); | ||
|
||
// Main | ||
// :: String -> NC () String String | ||
const readAllFiles = Fn.pipe([ | ||
getpath, // :: Path | ||
readFile, // :: NC () String String | ||
NC.map(Str.lines), // :: NC () String [String] | ||
NC.map(Arr.map(getpath)), // :: NC () String [Path] | ||
NC.chain(Arr.traverse(NC)(readFile)), // :: NC () String [String] | ||
NC.map(Str.join('')), // :: NC () String String | ||
]); | ||
|
||
// :: NC () String String | ||
const result = readAllFiles('index.txt'); | ||
|
||
// Remember that: | ||
// :: type NC r e x = Cont r (NodeEither e x) | ||
// so... | ||
// :: NC () String String = Cont () (NodeEither String String) | ||
// :: = ((NodeEither String String) -> ()) -> () | ||
|
||
// :: NodeEither String String -> ⊥ | ||
const fork = NodeEither.match({Left: exit1, Right: exit0}); | ||
const main = () => result(fork); // :: () | ||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
'use strict'; | ||
|
||
const path = require('path'); | ||
|
||
|
||
const derivemonad = M => { | ||
const {of, chain} = M; | ||
|
||
const map = f => chain(x => of(f(x))); | ||
const lift2 = f => mx => my => chain(x => chain(y => of(f(x)(y)))(my))(mx); | ||
|
||
return Object.assign({map, lift2}, M); | ||
}; | ||
|
||
const Arr = (() => { | ||
const of = x => [x]; | ||
const map = f => x => x.map(f); | ||
|
||
const foldl = f => z => xs => xs.reduce((p, c) => f(p)(c), z); | ||
|
||
const empty = []; | ||
const append = a => b => [...a, ...b]; | ||
|
||
const sequence = A => foldl(A.lift2(b => a => append(b)(of(a))))(A.of(empty)); | ||
const traverse = A => f => xs => sequence(A)(map(f)(xs)); | ||
|
||
return {map, foldl, traverse}; | ||
})(); | ||
|
||
const Fn = (() => { | ||
const id = x => x; | ||
const compose = f => g => a => f(g(a)); | ||
const flip = f => x => y => f(y)(x); | ||
const pipe = Arr.foldl(flip(compose))(id); | ||
|
||
return {id, compose, flip, pipe, '.': compose}; | ||
})(); | ||
|
||
const Str = (() => { | ||
const trim = s => s.trim(); | ||
const join = sep => arr => arr.join(sep); | ||
const split = sep => s => s.split(sep); | ||
const lines = Fn['.'](split('\n'))(trim); | ||
|
||
return {split, lines, join, trim}; | ||
})(); | ||
|
||
const Path = (() => { | ||
const combine = base => sub => path.join(base, sub); | ||
return {combine}; | ||
})(); | ||
|
||
// An Either interpretation of Node style first-element-falsy arrays | ||
// :: type NodeEither e d = [Maybe e, ...d] | ||
const NodeEither = (() => { | ||
const Left = l => [l]; | ||
const Right = (...r) => [null, ...r]; | ||
|
||
const match = ({Left, Right}) => ([e, ...x]) => e ? Left(e) : Right(...x); | ||
|
||
return {Left, Right, match}; | ||
})(); | ||
|
||
// The GenericEitherT monad transformer | ||
// :: type GenericEitherT e m l r = m (e l r) | ||
const GenericEitherT = E => M => { | ||
const {Left, Right, match} = E; | ||
|
||
// :: x -> m (e l x) | ||
const of = x => M.of(Right(x)); | ||
|
||
// :: (a -> m (e l b)) -> m (e l a) -> m (e l b) | ||
const chain = f => | ||
Fn['.'](M.chain)(match)({ | ||
Left: l => M.of(Left(l)), | ||
Right: f, | ||
}); | ||
|
||
return derivemonad({of, chain}); | ||
}; | ||
|
||
// The continuation monad | ||
// :: type Cont r a = (a -> r) -> r | ||
const Cont = (() => { | ||
const of = x => cb => cb(x); | ||
const chain = f => m => cb => m(x => f(x)(cb)); | ||
|
||
return derivemonad({of, chain}); | ||
})(); | ||
|
||
module.exports = {Arr, Fn, Str, Path, NodeEither, GenericEitherT, Cont}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,3 +60,4 @@ test coroutines-co.js | |
test coroutines-bluebird.js | ||
test await.js | ||
test futures.js | ||
test callbacks-revenge.js |