Skip to content

Instantly share code, notes, and snippets.

@n8gray

n8gray/MonadTest.swift

Last active Aug 29, 2015
Embed
What would you like to do?
Demonstrates using a cell to work around Swift compiler limitations.
/*
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