Last active
October 31, 2020 14:37
-
-
Save jonah-williams/5bc6e003f7147608e488 to your computer and use it in GitHub Desktop.
Implementing a Ruby `tap` equivalent in Swift
This file contains 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
// I wanted an equivalent to Ruby's `tap` (http://ruby-doc.org/core-2.3.0/Object.html#method-i-tap) in Swift which I could mix into any type to tap into method chains. | |
// Define the interface we want to provide as a protocol | |
private protocol Tap { | |
func tap(block: (Self) -> Void) -> Self | |
} | |
// Extend the `Tap` protocol with a default implementation | |
private extension Tap { | |
func tap(block: (Self) -> Void) -> Self { | |
block(self) | |
return self | |
} | |
} | |
// -------- | |
// Each type must declare adoption of the protocol with an extension | |
extension Array : Tap {} | |
let squares = [1, 2, 3, 4, 5].tap({ print($0) }).map({ $0*$0 }).tap({ print($0) }) | |
// > [1, 2, 3, 4, 5] | |
// > [1, 4, 9, 16, 25] | |
// -------- | |
// Suppose we had a class with some functions we wish we could chain together... | |
class Door : CustomDebugStringConvertible { | |
enum DoorState: String { | |
case open | |
case closed | |
} | |
enum LockState: String { | |
case locked | |
case unlocked | |
} | |
private var state = DoorState.open | |
private var lockState = LockState.unlocked | |
var debugDescription: String { | |
get { | |
return "The Door is: \(state) and \(lockState)" | |
} | |
} | |
func open() { | |
state = .open | |
} | |
func close() { | |
state = .closed | |
} | |
func lock() { | |
lockState = .locked | |
} | |
func unlock() { | |
lockState = .unlocked | |
} | |
func tap(block: (Door) -> Void) -> Self { | |
block(self) | |
return self | |
} | |
} | |
// Declare adoption of the `Tap` protocol | |
extension Door: Tap {} | |
let door = Door() | |
// Use `tap` to access the object without breaking the chain: | |
door.tap({ $0.close() }).lock() | |
print(door) | |
// > The Door is: closed and locked |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment