Skip to content

Instantly share code, notes, and snippets.

@jlieske
Created February 17, 2015 06:33
Show Gist options
  • Select an option

  • Save jlieske/3a5aa4e72e3467064544 to your computer and use it in GitHub Desktop.

Select an option

Save jlieske/3a5aa4e72e3467064544 to your computer and use it in GitHub Desktop.
Monadic while loop
//
// TryThenWhile.swift
// TryThenWhile
//
// Created by Jay Lieske on 2015.02.16.
// Copyright (c) 2015 Jay Lieske. All rights reserved.
//
import Cocoa
import XCTest
/// Object wrapper to work around Swift 1.1 limitations with generic enums.
public final class Box<T> {
/// The wrapped value.
public let unbox: T
/// Wrap a value.
public init(_ value: T) {
self.unbox = value
}
}
func ==<T : Equatable>(lhs: Box<T>, rhs: Box<T>) -> Bool {
return lhs.unbox == rhs.unbox
}
/// An Either type for Foundation programming.
/// The Success case holds the value of a computation.
/// The Failure case holds an NSError.
public enum Result<SuccessType> {
/// Represents a successful computation.
/// Wrapped in a Box because of Swift 1.1 limitations.
case Success(Box<SuccessType>)
/// Represents a failed computation.
case Failure(NSError)
/// Create a Success.
public init(_ value: SuccessType) {
self = .Success(Box(value))
}
/// Create a Failure.
public init(error: NSError) {
self = .Failure(error)
}
/// Apply a function if Success
public func map<U>(f: (SuccessType) -> U) -> Result<U> {
switch self {
case Success(let boxedValue):
return .Success(Box(f(boxedValue.unbox)))
case Failure(let error):
return .Failure(error)
}
}
/// Apply a function if Success
public func flatMap<U>(f: (SuccessType) -> Result<U>) -> Result<U> {
switch self {
case Success(let boxedValue):
return f(boxedValue.unbox)
case Failure(let error):
return .Failure(error)
}
}
}
/// Methods for Result sequencing and control flow.
extension Result {
/// Perform a step if Success, wrapping the result.
public func then<U>(f: (SuccessType) -> U) -> Result<U> {
return self.map(f)
}
/// Perform a step if Success.
public func then<U>(f: (SuccessType) -> Result<U>) -> Result<U> {
return self.flatMap(f)
}
/// Perform a loop if Success, breaking out of the loop on Failure.
public func thenWhile(test: @autoclosure () -> Bool, body: () -> Result<()>) -> Result<()> {
switch self {
case Failure(let error):
return .Failure(error)
case Success(let _):
while test() {
switch body() {
case Failure(let error):
return .Failure(error)
case Success(let _):
break;
}
}
return Result<()>(())
}
}
/// Perform a loop if Success.
public func thenWhile(test: @autoclosure () -> Bool, body: () -> ()) -> Result<()> {
switch self {
case Failure(let error):
return .Failure(error)
case Success(let _):
while test() {
body()
}
return Result<()>(())
}
}
}
class TryThenWhile: XCTestCase {
func testResultWhile() {
// Given
let s1: String = "hello"
let e1 = NSError(domain: "JaySwift", code: 1, userInfo: nil)
// When
var l1 = 0
let rs1 = Result(s1)
.thenWhile (l1 < 10) {
l1 += 1
}
.then {l1}
var l2 = 0
let fe2 = Result<String>(error: e1)
.thenWhile (l2 < 10) {
l2 += 1
}
.then {l2}
var l3 = 0
let fe3 = Result(s1)
.thenWhile (l3 < 10) {
Result(error: e1)
}
.then {l3}
var l4 = 0
let rs4 = Result(s1)
.thenWhile (l4 < 10) {
Result(l4 += 1)
}
.then {l4}
// Then
switch rs1 {
case .Success(let res): XCTAssertEqual(res.unbox, 10, "Test 1 should have succeeded")
case .Failure(_): XCTFail("Test 1 failed")
}
switch fe2 {
case .Success(_): XCTFail("Test 2 should have failed")
case .Failure(_): break
}
switch fe3 {
case .Success(_): XCTFail("Test 3 should have failed")
case .Failure(_): break
}
switch rs4 {
case .Success(let res): XCTAssertEqual(res.unbox, 10, "Test 4 should have succeeded")
case .Failure(_): XCTFail("Test 4 failed")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment