Skip to content

Instantly share code, notes, and snippets.

@saulshanabrook
Last active August 29, 2019 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saulshanabrook/9c5a5c8ef2eb4fa7050a2b0ed4225634 to your computer and use it in GitHub Desktop.
Save saulshanabrook/9c5a5c8ef2eb4fa7050a2b0ed4225634 to your computer and use it in GitHub Desktop.
export const INVALID = Symbol("INVALID");
/**
* Is able to optionally parse from type T to U
* and then create U from T.
*
* The idea is that these functions are inverses of each other:
*
* parser.create(parser.parse(t)) == t
*
* as long as parse doesn't return INVALID and
*
* parser.parse(parser.create(u)) == u
*/
class Parser<T, U> {
constructor(
public parse: (initial: T) => U | typeof INVALID,
public create: (parsed: U) => T
) {}
static identity<T>(): Parser<T, T> {
return new Parser(initial => initial, parsed => parsed);
}
/**
* Chains another parser after the current one.
*
* The idea is that:
*
* Parser(f, g).then(Parser(h, i)) == Parser(h∘f, g∘i)
*
* Assuming the composition operator deals with invalid like an optional type.
*
*/
then<V>(nextParse: Parser<U, V>): Parser<T, V> {
return new Parser<T, V>(
initial => {
const res = this.parse(initial);
if (res == INVALID) {
return INVALID;
}
return nextParse.parse(res);
},
parsed => this.create(nextParse.create(parsed))
);
}
}
@shiftyp
Copy link

shiftyp commented Aug 29, 2019

This looks similar, but it's a little too academic for me to parse through fully 🤷‍♂️https://en.wikipedia.org/wiki/Adjoint_functors

@saulshanabrook
Copy link
Author

@shiftyp thank you! That's exactly what I was looking for... I have ended up just making a specialized version of this for my use case, because otherwise I think the terminology is too much to grok...

Hopefully someday we will be able to express abstract concepts like this without getting weird and academic-y!

@shiftyp
Copy link

shiftyp commented Aug 29, 2019

I second that hope! That sounds like the right approach. There's a way to make things super generic, but if it hurts understandability then its not all that useful in aggregate. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment