Last active
August 29, 2015 14:02
-
-
Save n8gray/f58afbf629259e86650d to your computer and use it in GitHub Desktop.
Demonstrates using a cell to work around Swift compiler limitations.
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
/* | |
It appears that the Swift code generator requires fixed layout in a lot of contexts | |
where one might reasonably desire to use generics. Trying to do so gets you a | |
lovely error of this sort: | |
LLVM ERROR: unimplemented IRGen feature! non-fixed multi-payload enum layout | |
This is my hacky solution to that problem. I use a cell to encapsulate the generic | |
as a fixed-layout value. I've provided a prefix * operator to retrieve the value | |
(how retro!) in order to minimize the syntactic overhead of using the cell class. | |
*/ | |
// A cell that can hold a value. Use *cell to retrieve it. | |
// Meant to be immutable, though Swift has no immutable arrays... | |
class Cell<A> | |
{ | |
// For some reason you can't make a polymorphic field, so we make an array instead | |
// New convention: Private fields use the 💩 prefix | |
// (Best viewed in Safari, which correctly renders the pile-of-poo glyph.) | |
let 💩: A[] | |
// Create a cell that holds the given value | |
init(_ x:A) { self.💩 = [x] } | |
} | |
// Retrieve the value stored in a cell | |
// I tried postfix ! and !! to match optionals but the compiler wouldn't allow it | |
operator prefix * {} | |
@prefix func * <A> (c:Cell<A>) -> A | |
{ | |
return c.💩[0] | |
} | |
// A result enum. | |
// Use cells to work around LLVM ERROR: unimplemented IRGen feature! non-fixed multi-payload enum layout | |
enum Result<T, ErrT> | |
{ | |
case Ok(Cell<T>) | |
case Error(Cell<ErrT>) | |
// Convenience functions so we can hide our shame | |
static func ok(x:T) -> Result<T, ErrT> { return .Ok(Cell(x)) } | |
static func error(x:ErrT) -> Result<T, ErrT> { return .Error(Cell(x)) } | |
} | |
// Bind -- chain the result of a previous computation with a new one, but only if the result isn't Error | |
operator infix >>= { associativity left } | |
func >>= <A,B,ErrT> ( y : Result<A,ErrT>, f : A -> Result<B,ErrT> ) -> Result<B,ErrT> | |
{ | |
switch y { | |
case let .Ok(cell): return f(*cell) | |
case let .Error(cell): return .Error(cell) // It would be nice to just return lhs... | |
} | |
} | |
// // // // // // // // // // // // // // // // // // // // // // // // | |
// A simple game of skill | |
func rollTheDice(y:Int) -> Result<Int,String> | |
{ | |
let x = Int(arc4random_uniform(10)) | |
println("Rolled a \(x)") | |
switch x { | |
case 0: return Result.error("Game Over with Total: \(y)") | |
default: | |
let total = x + y | |
println("So far so good. Total: \(total)") | |
return Result.ok(x+y) | |
} | |
} | |
// If you reach the bonus round your score gets doubled! | |
func bonusRound(score:Int) -> Result<Int, String> | |
{ | |
let score2 = 2 * score | |
println("You reached the bonus round!! Score -> \(score2)") | |
return Result.ok(score2) | |
} | |
func testMonads() | |
{ | |
println("Testing monads") | |
arc4random_stir() | |
// Let's play! | |
let result = rollTheDice(0) | |
>>= rollTheDice | |
>>= rollTheDice | |
>>= bonusRound | |
>>= rollTheDice | |
>>= rollTheDice | |
>>= rollTheDice | |
>>= bonusRound | |
>>= rollTheDice | |
>>= rollTheDice | |
>>= rollTheDice | |
>>= bonusRound | |
switch result { | |
case let .Ok(cell): | |
println("Wow, you're really lucky! The grand total was: \(*cell)") | |
case let .Error(s): | |
println("Thanks for playing: \(*s)") | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment