Sajjon
(Alexander Cyon)
1
This code does not scale, and I thought I can write it using variadic generics...
public protocol Function {
associatedtype Input
associatedtype Output
func call(_ input: Input) -> Output
}
/// Returns `b(a)`, where `a` and `b` are types conforming to `Function`
public func pipe<A, B>(
_ a: A,
_ b: B
) -> ((A.Input) -> B.Output)
where
A: Function,
B: Function,
A.Output == B.Input
{
{ (f: A.Input) -> B.Output in
b.call(
a.call(f)
)
}
}
/// Returns `c(b(a))`, where `a`, `b` and `c` are types conforming to `Function`
public func pipe<A, B, C>(
_ a: A,
_ b: B,
_ c: C
) -> ((A.Input) -> C.Output)
where
A: Function,
B: Function,
C: Function,
A.Output == B.Input, B.Output == C.Input
{
{ (f: A.Input) -> C.Output in
c.call(
b.call(
a.call(f)
)
)
}
}
While I've read most pitches about variadic generics, I have not tried using it it earlier.
So I quite immediately felt lost... I need to express the input of a type is the output of the previous type, but don't think that kind of syntax for type constraints is brought up in SE-0393
public func pipe<First: Function, each M: Function, Last: Function>(
first: First,
middle: repeat each M,
last: Last
) -> ((F.Input) -> L.Output) where repeat (each M).Output == (each M+1).Input // Syntax?!?
{
fatalError("Syntax?! Feels like I wanna use a macro to implement this... LOL.")
}
It feels like... like I wanna create a pack of pair of types...
Never mind how lost I am (quite lost), is my goal achievable with the variadic generics tool shipped in Swift 5.9?
1 Like
Sajjon
(Alexander Cyon)
2
”func reduce” is maybe a better name. The function reduces multiple types conforming to Function into a new single one: (First.Input) -> Last.Output
aggie33
3
It is certainly not possible.
However, in some hypothetical future, you could use this awful workaround:
public func pipe<First: Function, each AllOtherThanLast: Function, each Item: Function, each AllOtherThanFirst: Function, Last: Function>(_ functions: repeat each Item) -> (First.Input) -> (Last.Output) where (First, repeat each AllOtherThanFirst) == (repeat each Item), (repeat each AllOtherThanLast, Last) == (repeat each Item), (repeat each AllOtherThanLast.Output) == (repeat each AllOtherThanFirst.Input) {
fatalError("Don't get me started on the implementation")
}
mlienert
(Martial Lienert)
4
Didn't manage to find a solution with Variadic Generics at the moment but find a way to do it with Result Builder
protocol Function {
associatedtype Input
associatedtype Output
func call(_ input: Input) -> Output
}
@resultBuilder
struct FunctionBuilder {
static func buildPartialBlock<F: Function>(first: F) -> (F.Input) -> F.Output {
first.call
}
static func buildPartialBlock<Input, Output, Next: Function>(
accumulated: @escaping (Input) -> Output,
next: Next
) -> (Input) -> Next.Output where Next.Input == Output {
{ (input: Input) in
next.call(accumulated(input))
}
}
}
func reduce<Input, Output>(
@FunctionBuilder _ builder: () -> (Input) -> Output
) -> (Input) -> Output {
builder()
}
struct FuncRandom: Function {
func call(_ value: UInt) -> Int {
Int.random(in: 0..<Int(value))
}
}
struct FuncToString: Function {
func call(_ value: Int) -> String {
"\(value)"
}
}
struct FuncFromString: Function {
func call(_ value: String) -> Int {
Int(value) ?? 0
}
}
let test = reduce {
FuncRandom()
FuncToString()
FuncFromString()
}
test(25)
1 Like
Sajjon
(Alexander Cyon)
5
Thx! Yes I realized it the other day when I talked to @GreatApe about it, and using ResultBuilder is what I am using it for!
Only that I wrote that ResultBuilder before the buildPartial was introduced 
mlienert
(Martial Lienert)
6
Note that if you want to keep all the types composing your pipeline you can with variadic generic and the result builder buildPartialBlock approach 
1 Like
dmt
(Dima Galimzianov)
7
I guess you might use a custom operator to achieve the same result
Sajjon
(Alexander Cyon)
8
Haha that is the next step, no need for a superficial protocol, will try to pipe output if any arity to input of same pattern.
Sajjon
(Alexander Cyon)
9
I used |> infix operator before