Need to define convenient predicate operation on Sequence, but cannot do `Sequence.predicate`, what's the best place to define?

extension Sequence {
    // my operation that takes a predicate
    func doIt(_ p: (Element, Element) -> Bool) {
        // just for example, do this
        forEach { print(" result: \(p($0, $0))") }
    }
    // pre-define some predicate for convenience
    // Question: where is the best place to put this? Try to avoid define as global func
    static func isConsecutive<T: BinaryInteger>(_ a: T, _ b: T) -> Bool { b - a == 1 }
}


// this do not compile:
[1, 2].doIt(Sequence.isConsecutive)
// Compile error:
// Protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements
// Static member 'isConsecutive' cannot be used on protocol metatype 'Sequence.Protocol'

// had to do it this way:
[1, 2].doIt([Int].isConsecutive)
// is there any better location to define `isConsecutive`

where is the best place to define my "prefab'ed" predicate func?

Hi @young

Since that predicate isn't tied to any particular type (or to sequences at all, really), defining it as a global function is your best bet. What's motivating trying to avoid making it global?

I guess you want default predicates to be suggested with dot notation. If so, you need to make them available as static properties/methods on a nominal type. You can define a Predicate type and have two overloads of doIt: one accepting closures and one accepting Predicates (I have mixed feelings about this approach though).

extension Sequence {
  func doIt(_ predicate: (_ previous: Element, _ next: Element) -> Bool) -> Bool {
    zip(self, self.dropFirst()).allSatisfy(predicate)
  }
  
  func doIt(_ predicate: Predicate<Element>) -> Bool {
    doIt(predicate.function)
  }
}

struct Predicate<Element> {
  let function: (Element, Element) -> Bool
  
  private init(predicate: @escaping (Element, Element) -> Bool) {
    self.function = predicate
  }
}

Then you can extend Predicate conditionally:

extension Predicate where Element: BinaryInteger {
  static var allConsecutive: Predicate {
    .init { p, n in n - p == 1 }
  }
}

extension Predicate where Element: Collection, Element.SubSequence: Equatable {
  static func allSamePrefix(ofMaxLength length: Int) -> Predicate {
    .init { p, n in p.prefix(length) == n.prefix(length) }
  }
}

With those extensions, with an array of Ints you will only get .allConsecutive, while with an array of Strings you will only get .allSamePrefix(ofMaxLength:).

[1, 2, 3, 4, 5, 6].doIt(.allConsecutive)
["Swift", "Scala"].doIt(.allSamePrefix(ofMaxLength: 1))
1 Like

Update: my solution:

extension Sequence where Element: Equatable {
    func doItEqual() {
        doIt(==)
    }
}

extension Sequence where Element: BinaryInteger {
    func doItConsecutive() {
        doIt({ $1 - $0 == 1 })
    }
}

=========================

So I am wrong in thinking my "predicate" func's belong to Sequence since my operation doIt is a func of Sequence. If the predicates are not really tied to Sequence, then they don't need to be name space inside Sequence. They can just be global and named with my prefix:

func doItIsConsecutive(...) -> Bool { .. }

I need to study @xAlien95 example and see if I can apply it to my code.

Thank you!

Terms of Service

Privacy Policy

Cookie Policy