Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions swift/Diffuser/Sources/Diffuser/diffuser/Combinators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,38 @@ public extension Diffuser {
return Diffuser(effect: effect)
}

/// Create a Diffuser which merges a list of Diffusers and always executes them regardless of the value.
///
/// This function is useful as a building block for more complex Diffusers, but it is unlikely that you would use
/// it on its own. Consider using `into` instead.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sentence still relevant in those two new variants of intoAlways?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, perhaps you could document briefly the intended use case of intoAlways for non-Equatable models with Equatable members.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! I think that sentence would still apply in this case but it's not really that clear so I replaced it to point out about the existence of intoAll instead.

///
/// - Parameter children: The list of Diffusers to merge
/// - Returns: A merged Diffuser which forwards any values it is `run` with to all
/// its children.
static func intoAlways(
_ children: [Diffuser<A>]
) -> Diffuser<A> {
.intoAlways { value in
for child in children {
child.run(value)
}
}
}

/// Create a Diffuser which merges a list of Diffusers and always executes them regardless of the value.
///
/// This function is useful as a building block for more complex Diffusers, but it is unlikely that you would use
/// it on its own. Consider using `into` instead.
///
/// - Parameter children: The list of Diffusers to merge
/// - Returns: A merged Diffuser which forwards any values it is `run` with to all
/// its children.
static func intoAlways(
_ children: Diffuser<A>...
) -> Diffuser<A> {
.intoAlways(children)
}

/// Add an additional layer of caching to an existing Diffuser.
///
/// This function is useful as a building block for more complex Diffusers, but it is unlikely that you would use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,88 @@ class IntoPropertiesTest: XCTestCase {
)
}

func testIntoAlwaysList() {
property("Running `intoAlways` on a list of `intoAlways` Diffusers is the same as running a list of `intoAlways` Diffusers individually in order") <-
forAll(integerLists, integersBetween0And10) { input, d in
var outputLhs: [Int] = []
let range = (0...d)
let diffusersListLhs = range.map { (i: Int) -> Diffuser<Int> in
let diffuser: Diffuser<Int> = .intoAlways { _ in outputLhs.append(i) }
return diffuser
}

var outputRhs: [Int] = []
let diffusersListRhs = range.map { (i: Int) -> Diffuser<Int> in
let diffuser: Diffuser<Int> = .intoAlways { _ in outputRhs.append(i) }
return diffuser
}
let diffuserRhs = Diffuser.intoAlways(diffusersListRhs)

input.forEach { value in
diffusersListLhs.forEach { diffuser in diffuser.run(value) }

diffuserRhs.run(value)
}

XCTAssertEqual(outputRhs, outputLhs)
return outputLhs == outputRhs
}
}

func testIntoAlwaysListMapInto_intoAllMapInto_equivalence() {
property("Running `intoAlways` on a list of `map -> into` Diffusers is the same as running `intoAll` on a list of `map -> into` Diffusers") <-
forAll(integerLists, integersBetween0And10) { input, d in
var outputLhs: [Int] = []
let diffuserLhs: Diffuser<Int> = .intoAlways(
.map(\.bitWidth, .into { _ in outputLhs.append(d) }),
.map({ $0 * 2 }, .into { _ in outputLhs.append(d + 1) }),
.map({ $0 % 2 == 0 }, .into { _ in outputLhs.append(d + 2) })
)

var outputRhs: [Int] = []
let diffuserRhs: Diffuser<Int> = .intoAll(
.map(\.bitWidth, .into { _ in outputRhs.append(d) }),
.map({ $0 * 2 }, .into { _ in outputRhs.append(d + 1) }),
.map({ $0 % 2 == 0 }, .into { _ in outputRhs.append(d + 2) })
)

input.forEach { value in
diffuserLhs.run(value)
diffuserRhs.run(value)
}

XCTAssertEqual(outputRhs, outputLhs)

input.forEach { value in
diffuserLhs.run(value)
diffuserRhs.run(value)
}

XCTAssertEqual(outputRhs, outputLhs)

input.forEach { value in
diffuserLhs.run(value + 1)
diffuserRhs.run(value + 1)
}

XCTAssertEqual(outputRhs, outputLhs)

return outputLhs == outputRhs
}
}

func testIntoAlwaysWithVarArgsIsSameAsIntoAlwaysWithList() {
property("intoAll with varargs is the same as intoAll with list") <-
// intoAlways(a, a, a) == intoAlways([a, a, a])
diffusersBehaveTheSame(
formula(
lhs: { diffuser in .intoAlways(diffuser, diffuser, diffuser) },
rhs: { diffuser in .intoAlways([diffuser, diffuser, diffuser]) }
)
)
}


func testDiffuserInitializerWithListIsSameAsIntoAllWithList() {
property("Diffuser initializer with list is the same as intoAll with list") <-
// Diffuser([a, a, a]) == intoAll([a, a, a])
Expand Down
Loading