Как использовать buildExpression в построителях функций Swift 5.2?
Я понимаю, что это черновик предложения. Я попытался реализовать простой DSL для построения строки, например так:
@_functionBuilder
struct StringBuilder {
static func buildExpression(_ string: String) -> [String] {
[string]
}
static func buildBlock(_ children: [String]...) -> [String] {
children.flatMap{ $0 }
}
}
func s(separator: String = "", @StringBuilder _ makeString: () -> [String]) -> String {
makeString().joined(separator: separator)
}
let z = s(separator: " ") {
"this"
"is"
"cool"
}
Тем не менее, компилятор жалуется, что "String" не конвертируется в "[String]". Это заставляет меня верить, что buildBlock
это единственная часть предложения, которое в настоящее время реализовано. (Это понятно, учитывая, что в SwiftUI они строят иерархию представлений, так что это все, что им нужно.)
Это правильно или я что-то не так делаю? Как правильно использовать buildExpression
?
Ответ ielyamani показывает, как создать построитель рабочей строки, который я использовал в моем примере выше. Однако это не решает актуальную проблему. Я не пытаюсь построить строитель строк. Я пытаюсь выяснить функции строителей. Строитель строк - только пример. Например, если мы хотим иметь построитель строк, который принимает целые числа, мы можем теоретически сделать следующее:
@_functionBuilder
struct StringBuilder {
static func buildExpression(_ int: Int) -> [String] {
["\(int)"]
}
// The rest of it implemented just as above
}
В этом случае, когда компилятор обнаружил Int
это будет называть buildExpression
затем выплюнуть наш тип компонента, в этом случае [String]
, Но, как сказал Мартин Р в комментарии к этому вопросу, buildExpression
в настоящее время не реализовано.
2 ответа
Я столкнулся с той же проблемой сегодня, кажется, что buildExpression не реализован. Я закончил тем, что сделал обходной путь, используя протокол "ComponentProtocol", а затем создал "Expression: ComponentProtocol" и "Component: ComponentProtocol". Это работает для меня на данный момент. Я надеюсь, что это будет реализовано позже.
protocol ComponentProtocol: ExpressibleByIntegerLiteral, ExpressibleByStringLiteral {
var value: String { get }
}
struct Expression: ComponentProtocol {
let _value: String
var value: String { _value }
init(_ value: String) { _value = value }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) {_value = String(describing: value) }
}
struct Component: ComponentProtocol {
let _values: [String]
var value: String { _values.joined(separator: ", ") }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) { _values = [String(describing: value)] }
init<T: ComponentProtocol>(_ values: T...) { _values = values.map { $0.value } }
init<T: ComponentProtocol>(_ values: [T]) { _values = values.map { $0.value } }
}
@_functionBuilder struct StringReduceBuilder {
static func buildBlock<T: ComponentProtocol>(_ components: T ...) -> Component { Component(components) }
static func buildEither<T: ComponentProtocol>(first: T) -> Component { Component(first.value) }
static func buildEither<T: ComponentProtocol>(second: T) -> Component { Component(second.value) }
static func buildOptional<T: ComponentProtocol>(_ component: T?) -> Component? {
component == nil ? nil : Component(component!.value)
}
}
func stringsReduce (@StringReduceBuilder block: () -> Component) -> Component {
return block()
}
let result = stringsReduce {
Expression(3)
"one"
Expression(5)
Expression("2")
83
}
let s2 = stringsReduce {
if .random () { // random value Bool
Expression(11)
} else {
Expression("another one")
}
}
Поскольку buildBlock(_:)
принимает переменное число массивов строк, это будет работать:
let z = s(separator: " ") {
["this"]
["is"]
["cool"]
}
Но это все еще неуклюже. Чтобы взять строки вместо массивов строк, добавьте эту функцию в StringBuilder
который принимает переменное количество строк:
static func buildBlock(_ strings: String...) -> [String] {
Array(strings)
}
И теперь вы можете сделать это:
let z = s(separator: " ") {
"Hello"
"my"
"friend!"
}
print(z) //Hello my friend!