Skip to content

Instantly share code, notes, and snippets.

@CodaFi
Last active March 23, 2017 15:47
Show Gist options
  • Save CodaFi/79889efa7defb438befee8e665f69868 to your computer and use it in GitHub Desktop.
Save CodaFi/79889efa7defb438befee8e665f69868 to your computer and use it in GitHub Desktop.
Typesafe format strings following Danvy, 1998
/// 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)
@jckarter
Copy link

Passing the closures out of withoutActuallyEscaping is undefined behavior; it will eventually trap.

@CodaFi
Copy link
Author

CodaFi commented Mar 23, 2017

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