Оберните функцию более высокого порядка в некоторый контекст и примените ее

#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 подход. Спасибо.