Skip to content

Instantly share code, notes, and snippets.

@KaQuMiQ
Created August 20, 2020 19:33
Show Gist options
  • Select an option

  • Save KaQuMiQ/49ca4c268e87b004a3364d8a1dfe0ce6 to your computer and use it in GitHub Desktop.

Select an option

Save KaQuMiQ/49ca4c268e87b004a3364d8a1dfe0ce6 to your computer and use it in GitHub Desktop.
Aegithalos 2.0
public struct Modifier<Subject, Failure: Error> {
@usableFromInline internal let modifier: (Subject, ParameterStore) -> Result<Subject, Failure>
}
// MARK: - init
public extension Modifier {
init(_ modifier: @escaping (Subject, ParameterStore) -> Result<Subject, Failure>) {
self.modifier = modifier
}
init(_ modifier: @escaping (Subject, ParameterStore) -> Subject) {
self.modifier = { subject, parameters in .success(modifier(subject, parameters)) }
}
init(_ modifier: @escaping (Subject) -> Result<Subject, Failure>) {
self.modifier = { subject, _ in modifier(subject) }
}
init(_ modifier: @escaping (Subject) -> Subject) {
self.modifier = { subject, _ in .success(modifier(subject)) }
}
init(_ modifiers: Modifier...) {
self.modifier = { subject, parameters in
var subject = subject
for modifier in modifiers {
switch modifier.modifier(subject, parameters) {
case let .success(modifiedSubject):
subject = modifiedSubject
case let .failure(error):
return .failure(error)
}
}
return .success(subject)
}
}
}
public extension Modifier where Subject: AnyObject {
init(_ modifier: @escaping (Subject, ParameterStore) -> Void) {
self.modifier = { subject, parameters in
modifier(subject, parameters)
return .success(subject)
}
}
init(_ modifier: @escaping (Subject) -> Void) {
self.modifier = { subject, _ in
modifier(subject)
return .success(subject)
}
}
}
public extension Modifier where Failure == Error {
init(_ modifier: @escaping (Subject, ParameterStore) throws -> Subject) {
self.modifier = { subject, parameters in
do {
return try .success(modifier(subject, parameters))
} catch {
return .failure(error)
}
}
}
init(_ modifier: @escaping (Subject) throws -> Subject) {
self.modifier = { subject, _ in
do {
return try .success(modifier(subject))
} catch {
return .failure(error)
}
}
}
}
// MARK: - apply
public extension Modifier {
@inlinable func apply(
on subject: Subject,
using parameters: ParameterStore = Parameters()
) -> Result<Subject, Failure> {
modifier(subject, parameters)
}
}
public extension Modifier where Subject: AnyObject {
@inlinable func apply(
on subject: Subject,
using parameters: ParameterStore = Parameters()
) {
_ = modifier(subject, parameters)
}
}
public extension Modifier where Failure == Never {
@inlinable func apply(
on subject: Subject,
using parameters: ParameterStore = Parameters()
) -> Subject {
switch modifier(subject, parameters) {
case let .success(subject):
return subject
case let .failure(never):
switch never {} // it never fails since Failure type is Never
}
}
}
// MARK: - combine
public extension Modifier {
@inlinable func combine(
_ other: @escaping (Subject, ParameterStore) -> Result<Subject, Failure>
) -> Self {
Self(self, Self(other))
}
@inlinable func combine(
_ other: @escaping (Subject) -> Result<Subject, Failure>
) -> Self {
Self(self, Self(other))
}
@inlinable func combine(
_ other: @escaping (Subject, ParameterStore) -> Subject
) -> Self {
Self(self, Self(other))
}
@inlinable func combine(_ other: @escaping (Subject) -> Subject) -> Self {
Self(self, Self(other))
}
@inlinable func combine(_ other: Self) -> Self {
Self(self, Self(other))
}
}
public extension Modifier where Subject: AnyObject {
@inlinable func combine(_ other: @escaping (Subject, ParameterStore) -> Void) -> Self {
Self(self, Self(other))
}
@inlinable func combine(_ other: @escaping (Subject) -> Void) -> Self {
Self(self, Self(other))
}
}
public extension Modifier where Failure == Error {
@inlinable func combine(_ other: @escaping (Subject, ParameterStore) throws -> Subject) -> Self {
Self(self, Self(other))
}
@inlinable func combine(_ other: @escaping (Subject) throws -> Subject) -> Self {
Self(self, Self(other))
}
}
// MARK: - parameters
public extension Modifier {
@inlinable static func parameter<P>(
_ parameter: P.Type,
_ modifier: @escaping (P.Value) -> Modifier
) -> Modifier where P: Parameter {
Self({ (subject: Subject, parameters: ParameterStore) -> Result<Subject, Failure> in
modifier(parameters[parameter]).modifier(subject, parameters)
})
}
@inlinable func parameter<P>(
_ parameter: P.Type,
_ modifier: @escaping (P.Value) -> Self
) -> Self where P: Parameter {
Self(self, .parameter(parameter, modifier))
}
}
// MARK: - transform
public extension Modifier {
@inlinable func contramap<OtherSubject>(
_ keyPath: WritableKeyPath<OtherSubject, Subject>
) -> Modifier<OtherSubject, Failure> {
Modifier<OtherSubject, Failure>({ otherSubject, parameters in
self.modifier(otherSubject[keyPath: keyPath], parameters)
.map { subject in
var other = otherSubject
other[keyPath: keyPath] = subject
return other
}
})
}
@inlinable func mapFailure<OtherFailure: Error>(
_ transform: @escaping (Failure) -> OtherFailure
) -> Modifier<Subject, OtherFailure> {
Modifier<Subject, OtherFailure>({ otherSubject, parameters in
self.modifier(otherSubject, parameters)
.mapError(transform)
})
}
@inlinable func mapFailure<OtherFailure: Error>(
_ transform: @escaping (Failure, ParameterStore) -> OtherFailure
) -> Modifier<Subject, OtherFailure> {
Modifier<Subject, OtherFailure>({ otherSubject, parameters in
self.modifier(otherSubject, parameters)
.mapError { error in
transform(error, parameters)
}
})
}
}
public extension Modifier where Subject: AnyObject {
@inlinable func contramap<OtherSubject>(
_ transform: @escaping (OtherSubject) -> Subject
) -> Modifier<OtherSubject, Failure> {
Modifier<OtherSubject, Failure>({ (otherSubject: OtherSubject, parameters: ParameterStore) -> Result<OtherSubject, Failure> in
self.modifier(transform(otherSubject), parameters)
.map { _ in otherSubject }
})
}
}
// MARK: - replace
public extension Modifier {
@inlinable static func replace(with subject: Subject) -> Self {
Self({ _, _ in .success(subject) })
}
@inlinable static func replace(with failure: Failure) -> Self {
Self({ _, _ in .failure(failure) })
}
@inlinable func replaceFailure(with subject: Subject) -> Modifier<Subject, Never> {
Modifier<Subject, Never>({ otherSubject, parameters in
self.modifier(otherSubject, parameters)
.flatMapError { _ in .success(subject) }
})
}
}
public protocol Parameter {
associatedtype Value
static var defaultValue: Value { get }
}
internal extension Parameter {
static var identifier: ObjectIdentifier { ObjectIdentifier(Self.Type.self) }
}
public struct Parameters {
internal var parameters: Dictionary<ObjectIdentifier, Any> = [:]
public init() {}
}
extension Parameters: ParameterStore {
public subscript<P: Parameter>(parameter: P.Type) -> P.Value {
get { parameters[parameter.identifier] as? P.Value ?? P.defaultValue }
set { parameters[parameter.identifier] = newValue }
}
}
public protocol ParameterStore {
subscript<P: Parameter>(parameter: P.Type) -> P.Value { get set }
}
public extension ParameterStore {
mutating func reset<P: Parameter>(parameter: P.Type) {
self[parameter] = parameter.defaultValue
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment