Last active
March 23, 2017 15:47
-
-
Save CodaFi/79889efa7defb438befee8e665f69868 to your computer and use it in GitHub Desktop.
Typesafe format strings following Danvy, 1998
This file contains hidden or 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
/// Playground - noun: a place where people can play | |
/// An un-parser for things is a continuation on strings... | |
precedencegroup CompositionPrecedence { | |
associativity: right | |
higherThan: BitwiseShiftPrecedence | |
} | |
prefix operator << | |
postfix operator >> | |
infix operator • : CompositionPrecedence | |
prefix func << <A>(_ x : A) -> A { return x } | |
postfix func >> <A>(_ x : A) -> A { return x } | |
func • <A, B, C>(_ f : @escaping (B) -> C, _ g : @escaping (A) -> B) -> (A) -> C { | |
return { x in f(g(x)) } | |
} | |
//: Olivier Danvy proposes a neat solution to a problem that would seem, at first blush, | |
//: to require dependent types to solve. Namely, a typesafe printf function, which is | |
//: usually implemented by parsing the format string into a functional that constructs an | |
//: arrow with a hole for each argument. Danvy instead uses continuation-passing to | |
//: construct a small automata capable of running format string "programs". The result is | |
//: the same, albeit with some minor amounts of syntactic sugar to make the whole thing | |
//: look nice (<< >>). | |
//: Inline string literals | |
func lit<A>(_ x : String) -> (@escaping (String) -> A) -> (String) -> A { | |
return { k in | |
return { s in k(s + x) } | |
} | |
} | |
//: \n | Appends a newline character. | |
//: | |
//: Takes a continuation passing the current string and returns a | |
//: continuation that appends a newline character to the string. | |
func eol<A>(_ k : @escaping (String) -> A) -> (String) -> A { | |
return { x in k(x + "\n") } | |
} | |
//: %d | Appends an integer to the format string. | |
func int<A>(_ k : @escaping (String) -> A) -> (String) -> (Int) -> A { | |
return { s in { x in k(s + x.description) } } | |
} | |
//: %s | Appends a string to the format string. | |
func str<A>(_ k : @escaping (String) -> A) -> (String) -> (String) -> A { | |
return { s in { x in k(s + x) } } | |
} | |
//: Execute the continuation stack and return the final format string. | |
func format<A>(_ f : (@escaping (String) -> String) -> (String) -> A) -> A { | |
return f({ $0 })("") | |
} | |
let x : Int = 2 | |
let y : Int = 21 | |
let z : Int = 42 | |
let s : String = ", right?" | |
let f = format(<<(int • lit(" * ") • int • lit(" = ") • int • str • eol)>>)(x)(y)(z)(s) |
Phooey. Tho, it can get annoying to annotate CPS'd things. You run into all kinds of fun corner cases.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Passing the closures out of
withoutActuallyEscaping
is undefined behavior; it will eventually trap.