Skip to content

Instantly share code, notes, and snippets.

@ukitaka
Last active February 17, 2017 11:24
Show Gist options
  • Save ukitaka/e7276162abb0310c297172910957e1e5 to your computer and use it in GitHub Desktop.
Save ukitaka/e7276162abb0310c297172910957e1e5 to your computer and use it in GitHub Desktop.
Readerモナド + PhantomTypeでRealmの処理を再利用可能かつ合成可能にする
import RealmSwift
import Result
// MARK: -
public protocol _Read {}
public struct Read: _Read { }
public protocol _Write {}
public struct Write: _Write { }
public enum RealmError: Error { /* TODO */ }
public typealias RealmResult<T> = Result<T, RealmError>
public struct RealmTxn<RW, T> {
fileprivate let _run: (Realm) -> RealmResult<T>
public init(_ _run: @escaping (Realm) -> RealmResult<T>) {
self._run = _run
}
public func map<S>(_ f: @escaping (T) -> S) -> RealmTxn<RW, S> {
return RealmTxn<RW, S> { realm in
self._run(realm).map(f)
}
}
public func ask() -> RealmTxn<RW, Realm> {
return RealmTxn<RW, Realm> { realm in
return .success(realm)
}
}
}
// MARK: - flatMap
public extension RealmTxn where RW: _Read {
// Read & Write -> Write
public func flatMap<W: _Write, S>(_ f: @escaping (T) -> RealmTxn<W, S>) -> RealmTxn<W, S> {
return RealmTxn<W, S> { realm in
self._run(realm).flatMap { t in f(t)._run(realm) }
}
}
// Read & Read -> Read
public func flatMap<S>(_ f: @escaping (T) -> RealmTxn<RW, S>) -> RealmTxn<RW, S> {
return RealmTxn<RW, S> { realm in
self._run(realm).flatMap { t in f(t)._run(realm) }
}
}
}
public extension RealmTxn where RW: _Write {
// Write & Any -> Write
public func flatMap<RW2, S>(_ f: @escaping (T) -> RealmTxn<RW2, S>) -> RealmTxn<RW, S> {
return RealmTxn<RW, S> { realm in
self._run(realm).flatMap { t in f(t)._run(realm) }
}
}
}
// MARK: - run
// Readのみの場合はトランザクションを作成する必要なし
public func runTxn<R, T>(realm: Realm, txn: RealmTxn<R, T>) -> RealmResult<T> where R: _Read {
return txn._run(realm)
}
// Writeのみの場合はトランザクションを作成
public func runTxn<W, T>(realm: Realm, txn: RealmTxn<W, T>) -> RealmResult<T> where W: _Write {
var result: RealmResult<T>! = nil // FIXME: これだからthrowは....
do {
try realm.write {
result = txn._run(realm)
}
} catch {
result = .failure(error as! RealmError)
}
return result
}
struct User {
let id: Int
let name: String
}
func findByID(id: Int) -> RealmTxn<Read, User> {
return RealmTxn { _ in
return .success(User(id: id, name: "AAA")) // dummy
}
}
func findByName(name: String) -> RealmTxn<Read, User> {
return RealmTxn { _ in
return .success(User(id: 100, name: name)) // dummy
}
}
func updateUser(user: User) -> RealmTxn<Write, Void> {
return RealmTxn { _ in
return .success(print(user)) // dummy
}
}
func changeUserName(_ newName: String) -> (User) -> User {
return { user in
return User(id: user.id, name: newName)
}
}
// Read & Write = Write
let t1: RealmTxn<Write, Void> = findByID(id: 123)
.map(changeUserName("BBB"))
.flatMap(updateUser)
// Read & Read = Read
let t2: RealmTxn<Read, User> = findByID(id: 123)
.map { $0.name }
.flatMap(findByName)
let id = 2
// Write & Read = Write
let t3: RealmTxn<Write, User> = updateUser(user: User(id: id, name: "BBB"))
.map(const(id))
.flatMap(findByID)
runTxn(realm: Realm(), txn: t3)
@ukitaka
Copy link
Author

ukitaka commented Feb 17, 2017

スレッドもPhantomTypeにすればより安全かもしれない

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment