#swift #functional-programming
#swift #функциональное программирование
Вопрос:
Я полагаю, что аналогично аппликативу, я хотел бы обернуть функцию более высокого порядка в некотором контексте и применить ее к последовательности.
protocol Foo { func a() -> Void }
class Bar: Foo { func a() { } }
let seq = [Bar(), Bar(), Bar()]
Конкретно, приведите три приведенных выше определения, я хотел бы иметь возможность вызывать dispatch(event)
where dispatch
forEach
завершает последовательность экземпляров протокола и event
является функцией, определенной этим протоколом.
private func dispatch(event: () -> Void) -> Void {
DispatchQueue.main.async { // context that the forEach mapping should happen inside of
seq.forEach($0.event())
}
}
let _ = dispatch(Foo.a)
Очевидно, что это не работает с системой типов Swift (я привык к apply() от Clojure). В качестве возможной альтернативы, есть ли способ обернуть последовательность в частичную, которую я могу forEach
использовать?
let dispatch() -> [Foo] {
DispatchQueue.main.async {
seq.forEach // 🤷🏻♂️
}
}
let _ = dispatch { $0.a() }
Возможно dispatch
, следует рассматривать как ограниченное расширение для Sequence
?
extension Sequence where Iterator.Element == Foo {
func dispatch() -> [Foo] {
DispatchQueue.main.async {
return self.forEach // 🤷🏾♀️
}
}
}
Комментарии:
1. Я не знаю терминологии функционального программирования, поэтому, возможно, я неправильно понял вопрос. Вы пытаетесь написать функцию, которая может вызывать
.a()
каждыйFoo
из последовательности, но независимо от того, основан ли он.a
или какой-либо другой метод на параметре отправки?2. @NewDev Это правильно! Я хочу, чтобы вызывающий мог указать
a()
или потенциальныйb()
и т. Д.3. Одним из подходов, который мог бы вам помочь, было использование ключевого пути, например
Bar.a
, но, к сожалению, ключевые пути не могут ссылаться на методы экземпляра4. Можете ли вы немного объяснить свою мотивацию? Я пытаюсь понять, чего вы пытаетесь достичь здесь, потому что мне непонятно, что является бессмысленным заполнителем, а что просто упрощающим примером. Один из таких вопросов: какова цель
Foo
? Почему вы хотите расширитьSequence
Foo
s?
Ответ №1:
Хотя это и не так элегантно, как могло бы быть с ключевыми путями к методам экземпляра, вы также можете создать диспетчерскую функцию, которая принимает замыкание с каждым элементом в качестве параметра:
func dispatch(_ handler: @escaping (Foo) -> Void) {
DispatchQueue.main.async {
seq.forEach(handler)
}
}
И вызовите ее следующим образом:
dispatch { $0.a() }
Ответ №2:
Хотя вы не можете использовать KeyPath
методы for instance, вы можете использовать их для свойств, если это работает для вас.
Вы можете изменить свой протокол и реализацию на что-то вроде этого:
protocol Foo {
var a: () -> Void { get }
}
class Bar: Foo {
lazy var a = {
print("Bar")
}
}
Затем вы можете определить свою dispatch
функцию, чтобы принимать KeyPath
параметр as:
extension Sequence where Iterator.Element: Foo {
func dispatch(keyPath: KeyPath<Foo, () -> Void>) {
DispatchQueue.main.async {
forEach { $0[keyPath: keyPath]() }
}
}
}
и передайте свойства KeyPath
в качестве параметра:
let seq = [Bar(), Bar(), Bar()]
seq.dispatch(keyPath: Foo.a)
Комментарии:
1. @NewDev хорошее предложение! Я обновил свой ответ. Я предпочел такой
lazy
подход. Спасибо.