Быстрая задача с составлением ленивых преобразований последовательности

Я продолжаю свой набег на Functional Swift и очень наслаждаюсь этим вызовом. Я работаю с преобразованиями, которые превращают элементы в ленивые последовательности.

Чтобы указать ошибку заранее, я получаю: Невозможно преобразовать значение типа 'Transform' (aka '(Int) -> LazySequence>') в ожидаемый тип аргумента '() -> LazySequence ‹[]> '

Моя проблема заключается в их составлении, но мне нужно дать некоторый контекст, чтобы показать проблему.

Вот трансформация:

typealias Transform<T, U> = (T) -> LazySequence<[U]>

И я могу определить Forward Application:

precedencegroup LazyForwardApplication {
  associativity: left
}

infix operator |~>: LazyForwardApplication

func |~> <T: LazySequenceProtocol, U>(
  input: T,
  transform: @escaping Transform<T.Elements.Element,U>
  ) -> LazySequence<FlattenSequence<LazyMapSequence<T.Elements, LazySequence<[U]>>>> {

  return input.flatMap(transform)
}

Тип возврата немного скучноват, но работает нормально:

let start = [10,20,30].lazy

let add4
let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030
6: Transform<Int, Int> = { let result = [ $0 + 4, $0 + 5, $0 + 6] print("> add4
let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030
6(\($0)) -> \(result)") return result.lazy }

// Обратите внимание, что я частично включил отладку, чтобы быть уверенным, что это происходит лениво.

let result1 = start |~> add4
let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030
6 result1.forEach{ print($0) } // 14, 15, 16, 24, 25, 26, 34, 35, 36

И еще один похожий пример:

let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030

И я могу связать их вместе в строке:

// Double application
let result3 = start |~> add4_5_6 |~> add7000_8000
result3.forEach{ print($0) }
// 7014, 8014, 7015, 8015, 7016, 8016,
// 7024, 8024, 7025, 8025, 7026, 8026,
// 7034, 8034, 7035, 8035, 7036, 8036

Но я бы тоже хотел уметь их сочинять:

// Forward Composition
precedencegroup LazyForwardComposition {
  associativity: right
}
infix operator >~>: LazyForwardComposition

func >~> <T, U: Sequence, V: Sequence>(
  left:  @escaping Transform<T,U>,
  right: @escaping Transform<U,V>
  ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {

  return { input in
    let b: LazySequence<[U]> = left(input)
    let c = b.flatMap(right)
    return c
  }
}

И вот где я получаю ошибку:

let composed = add4_5_6 >~> add7000_8000
// ERROR IN ABOVE LINE: Cannot convert value of type
'Transform<Int, Int>' (aka '(Int) -> LazySequence<Array<Int>>')
to expected argument type
'(_) -> LazySequence<[_]>'

let result4 = start |~> composed
result4.forEach{ print($0) }

Результат будет таким же, как и result3

Я работал над этим несколько раз, но все время застреваю. Любые мысли о том, как решить, приветствуются.

(Мой предыдущий вопрос - это аналогичная территория, но другая проблема: Swift: ленивая инкапсуляция цепочек map, filter, flatMap)

Для детской площадки:

typealias Transform<T, U> = (T) -> LazySequence<[U]>

// And I can define Forward Application:

precedencegroup LazyForwardApplication {
  associativity: left
}

infix operator |~>: LazyForwardApplication

func |~> <T: LazySequenceProtocol, U>(
  input: T,
  transform: @escaping Transform<T.Elements.Element,U>
  ) -> LazySequence<FlattenSequence<LazyMapSequence<T.Elements, LazySequence<[U]>>>> {

  return input.flatMap(transform)
}

// The return type is a bit of a mouthful but it works fine:

let start = [10,20,30].lazy

let add4_5_6: Transform<Int, Int> = {
  let result = [ $0 + 4, $0 + 5, $0 + 6]
  print("> add4_5_6(\($0)) -> \(result)")
  return result.lazy
}

// Note that I put the debug in partly so I can be sure that it's happening lazily.

let result1 = start |~> add4_5_6
result1.forEach{ print($0) }
// 14, 15, 16, 24, 25, 26, 34, 35, 36

// And another similar example:

let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030

// And I can chain these together inline: 

// Double application
let result3 = start |~> add4_5_6 |~> add7000_8000
result3.forEach{ print($0) }
// 7014, 8014, 7015, 8015, 7016, 8016,
// 7024, 8024, 7025, 8025, 7026, 8026,
// 7034, 8034, 7035, 8035, 7036, 8036

// But I'd like to be able to compose them too:

// Forward Composition
precedencegroup LazyForwardComposition {
  associativity: right
}
infix operator >~>: LazyForwardComposition

func >~> <T, U: Sequence, V: Sequence>(
  left:  @escaping Transform<T,U>,
  right: @escaping Transform<U,V>
  ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {

  return { input in
    let b: LazySequence<[U]> = left(input)
    let c = b.flatMap(right)
    return c
  }
}

// And here's where I get an error:

let composed = add4_5_6 >~> add7000_8000
// ERROR IN ABOVE LINE: Cannot convert value of type 'Transform<Int, Int>' (aka '(Int) -> LazySequence<Array<Int>>') to expected argument type '(_) -> LazySequence<[_]>'

let result4 = start |~> composed
result4.forEach{ print($0) }

// The result would come out the same as result3

person Cortado-J    schedule 26.02.2019    source источник


Ответы (1)


У меня есть частичный ответ, но, возможно, это поможет вам куда-то добраться.

Прежде всего, Transform<T, U> определяется как (T) -> LazySequence<[U]>, поэтому общие типы U и V не могут быть специализированы как Sequence:

func >~> <T, U, V>(
    left:  @escaping Transform<T,U>,
    right: @escaping Transform<U,V>
    ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {

    return { input in
        let b = left(input)
        let c = b.flatMap(right)
        return c
    }
}

Во-вторых, ваш оператор |~> принимает Transform в качестве параметра правой руки, поэтому вы не можете использовать его с типом возвращаемого значения параметра >~>. Я смог получить результат со следующей строкой:

let result4 = start.flatMap(composed)

Возможно, вы могли бы перегрузить оператор |~>, чтобы он принял правильный тип, но это выглядело бы некрасиво. Или, может быть, с достаточным количеством шрифтов :)

person pckill    schedule 26.02.2019
comment
Спасибо, @pckill. Я понимаю, почему специализация Sequence нуждается в удалении, а другие ваши комментарии полезны. Я отправлю ответ, когда разберусь с этим. - person Cortado-J; 27.02.2019