Created
December 8, 2015 23:06
-
-
Save mathonsunday/304baf3df67ecaf97660 to your computer and use it in GitHub Desktop.
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
| //: Playground - noun: a place where people can play | |
| import Cocoa | |
| import Foundation | |
| // WinterBuddies a social network to connect you with people who can help you survive the winter | |
| class ProfileViewController : UIViewController { | |
| let musicService = MusicService() | |
| init(musicService: MusicService) { | |
| self.musicService = musicService | |
| } | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| musicService.play() // start playlist on application | |
| } | |
| } | |
| // ProfileViewControllerTests.swift | |
| func testShouldPlayMusicWhenViewDidLoad() { | |
| // setup mock | |
| class MockMusicService : MusicService { | |
| var musicPlayed = false | |
| override func play() { | |
| musicPlayed = true | |
| } | |
| } | |
| var viewController = ProfileViewController(musicServicer: MockMusicService()) | |
| // invoke | |
| viewController.viewDidLoad() | |
| // verify | |
| XCTAssertTrue(mockMusicService.playCalled) | |
| } | |
| class MockMusicService: MusicService { | |
| class func basic() -> MusicService { | |
| return builder().build()! | |
| } | |
| class func builder() -> MusicServiceBuilder { | |
| return MusicServiceBuilder() | |
| .withApplication("Spotify") | |
| .withPlaylist("Chill Party") | |
| } | |
| } | |
| extension MusicServiceBuilder { | |
| func withApplication(application: String) -> MusicServiceBuilder { | |
| self.application = application | |
| return self | |
| } | |
| func withPlaylist(playlistName: String) -> MusicServiceBuilder { | |
| self.playlistName = playlistName | |
| return self | |
| } | |
| } | |
Author
Not clear to me why this is desirable
The series of computations are performed without necessarily unwrapping the environment as context from the Reader.
Does it necessarily lead to
This would allow the setup and logic for the particular Reader or Config in this case to be encapsulated and separate from the rest of the control flow.
So, for the test I'd do:
// ProfileViewControllerTests.swift
func testShouldPlayMusicWhenViewDidLoad() {
let mockConfigReader = Reader<Void, Config>.pure(MockConfig())
var viewController = ProfileViewController(configReader)
// invoke
viewController.viewDidLoad()
// verify
XCTAssertTrue(mockConfigReader.musicPlayFavoriteArtist) // our approach might not jibe well with this
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I tend to agree that the Reader monad may provide less value than in Haskell or Scala because of the lack of syntax support in Swift. Haskell do and Scala for-comprehension notation is great for this use case. I think this would also depend on the use case your going to use it in Swift. I'm not an expert, but it's definitely interesting to think about.
If you want constructor injection of only a single type then it may make sense to use simple constructor injection. I think the power of the Reader monad is about the idea that it represents. Given an environment E it will output the modified environment A. It's essentially a function E -> A. If we create our MusicService reader that takes in the environment Config and returns a MusicService.
This would allow the Config to be injected anywhere as a dependency while allowing computations on the monad to be performed without extracting the Config state from the Reader. As far as I understand it this is possible by implementing fmap, apply, and flatMap (bind >>-) for the monad in question, the Reader monad. The series of computations are performed without necessarily unwrapping the environment as context from the Reader.
Then if we change the definition slightly:
This might allow the configReader to be passed in anywhere it's needed but still allow the extraction of the needed environment while applying operations to it like a monad. This would allow the setup and logic for the particular Reader or Config in this case to be encapsulated and separate from the rest of the control flow.
The example might then be:
@mathonsunday Let me know if this makes sense or needs clarification.