0

I can create an iterator to create prefixes:

extension Array where Element == Int {
  func prefixesInt() -> AnyIterator<[Element]> {
    var length = 0
    return AnyIterator {
      guard length < self.count else { return nil }
      length += 1
      return Array(self.prefix(length))
    }
  }
}

for prefix in [1,10,5].prefixesInt() {
  print(prefix)  // Prints: [1]  then  [1, 10]  then [1, 10, 5]
}

Now I want to achieve the same with other types by:

1) Encoding the types to Ints

2) Using the same prefix function

3) Decoding back to the original type

[I know there might be other ways to achieve this but I want to follow this line for various reasons and also wanting to understand more.]

So I need:

extension Array where Element: Equatable {
  func encode() -> [Int] {
    return map { self.firstIndex(of: $0)! }
  }
}
print(["A", "B", "A", "C"].encode())  // Prints: [0, 1, 0, 3]

extension Array where Element == Int {
  func decode<Output>(_ original: [Output]) -> [Output] {
    return map { original[$0] }
  }
}
print([2,3,0].decode(["P", "Q", "R", "S"]))  // Prints: ["R", "S", "P"]

I can now do what I want with:

extension Array where Element: Equatable {
  func prefixes() -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return encode().prefixesInt().lazy.map { $0.decode(self) }
  }
}

for prefix in ["H","A","T"].prefixes() {
  print(prefix)
}

I now want to go a step further by making the transform (in this case prefixes) into a parameter and my attempt is:

extension Array where Element: Equatable {
  func encodeTransformDecode(transform: ([Int]) -> AnyIterator<[Int]> ) -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return transform(encode()).lazy.map { $0.decode(self) }
  }
}

which compiles fine but when I try:

extension Array where Element: Equatable {
  func prefixes2() -> LazyMapSequence<AnyIterator<[Element]>, [Element]> {
    return encodeTransformDecode(transform: prefixesInt) //ERROR: 'Array<Element>' is not convertible to 'Array<Int>'
  }
}

for prefix in ["A","B","C"].prefixes2() {
  print(prefix)
}

Then I get the error indicated

I'm stuck. Any help appreciated.

Whole code:

print("========== prefixesInt")
extension Array where Element == Int {
  func prefixesInt() -> AnyIterator<[Element]> {
    var length = 0
    return AnyIterator {
      guard length < self.count else { return nil }
      length += 1
      return Array(self.prefix(length))
    }
  }
}

for prefix in [1,10,5].prefixesInt() {
  print(prefix)  // Prints: [1]  then  [1, 10]  then [1, 10, 5]
}

print("============ encode")
extension Array where Element: Equatable {
  func encode() -> [Int] {
    return map { self.firstIndex(of: $0)! }
  }
}
print(["A", "B", "A", "C"].encode())  // Prints: [0, 1, 0, 3]

print("============ decode")
extension Array where Element == Int {
  func decode<Output>(_ original: [Output]) -> [Output] {
    return map { original[$0] }
  }
}
print([2,3,0].decode(["P", "Q", "R", "S"]))  // Prints: ["R", "S", "P"]

print("============ prefixes")
extension Array where Element: Equatable {
  func prefixes() -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return encode().prefixesInt().lazy.map { $0.decode(self) }
  }
}

for prefix in ["H","A","T"].prefixes() {
  print(prefix)
}

extension Array where Element: Equatable {
  func encodeTransformDecode(transform: ([Int]) -> AnyIterator<[Int]> ) -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return transform(encode()).lazy.map { $0.decode(self) }
  }
}

print("============ prefixes2")
extension Array where Element: Equatable {
  func prefixes2() -> LazyMapSequence<AnyIterator<[Element]>, [Element]> {
    return encodeTransformDecode(transform: prefixesInt)
  }
}

for prefix in ["A","B","C"].prefixes2() {
  print(prefix)
}
Cortado-J
  • 2,035
  • 2
  • 20
  • 32
  • 2
    `prefixesInt` is only defined on `Array where Element == Int` and not arrays in general – ielyamani Nov 16 '18 at 10:26
  • But encodeTransformDecode's signature is asking that transform has type: ([Int]) -> AnyIterator<[Int]> so not clear on the message? – Cortado-J Nov 16 '18 at 10:54
  • 1
    You can't call `prefixesInt` from an array where the only constraint on element is to be equatable. Plus the signature of `prefixesInt` is not `([Int]) -> AnyIterator<[Int]>` – ielyamani Nov 16 '18 at 10:58
  • What version of Xcode/Swift are you using? Commenting out your error at the end and running produces output at expected plus `SWIFT RUNTIME BUG: unable to demangle type of field '_transform'. mangled type name is 'q_7ElementSTQzc'` in Xcode 10.0/Swift 4.2 playground – CRD Nov 16 '18 at 11:08
  • Hi @CRD, Version is Xcode 10.1 (10B61) – Cortado-J Nov 16 '18 at 11:18
  • Thanks @Carpsen90 for your help with this. Your question makes me realise I'm not clear on the type of prefixesInt. I assumed because it was a method on `[Int]` and returns `AnyIterator<[Int]>`, that it's type would be `([Int]) -> AnyIterator<[Int]>` but I guess I'm not right? – Cortado-J Nov 16 '18 at 11:22
  • Its type is `() -> AnyIterator<[Int]>`. – ielyamani Nov 16 '18 at 11:26
  • 1
    I think one thing you are missing is that in `encodeTransformDecode(transform: prefixesInt)` there is an *implicit* `self` reference as `prefixesInt` is an instance method – and in this case that `self` is not of type `[Int]`. This gives you two problems, wrong type, and even if it was the right type you've implicitly bound the `self`. If you define a `static` `prefixesInt` method which takes an `[Int]` and simply calls the instance version on its arg, pass that to `encodeTransformDecode`, you'll get further. (I've got your code "working" in 10.0 but its spewing RUNTIME BUGs so won't post!) – CRD Nov 16 '18 at 11:49
  • Thanks @CRD. I had wondered about that but wasn't very clear on it so your comment nudged me to explore... I was very keen not to go the static route - it makes other parts of the code less elegant. I had a rather vague memory of some currying methods from the excellent "obj Functional Swift". So I looked it up and it helped me clarify that the type of `prefixesInt` is in fact: `(Array) -> () -> AnyIterator<[Int]>`. Realising that gives me a solution. I'll post my findings as an Answer. – Cortado-J Nov 16 '18 at 19:50

2 Answers2

1

As indicated in my comment and you've explored in your own answer, the core problem in your code is that you had the type of prefixesInt incorrect. An instance method has a type of the form:

(<object type>) -> (<argument types>) -> <return type>

the value passed for <object type> is what is bound to self in the function. So the type prefixesInt is:

([Int]) -> () -> AnyIterator<[Int]>

To fix your code you only need to change prefixes2:

func prefixes2() -> LazyMapSequence<AnyIterator<[Int]>, [Element]>
{
   return encodeTransformDecode(transform: { $0.prefixesInt() } )
}

The type has changed to include AnyIterator<[Int]> rather than AnyIterator<[Element]> and a closure { $0.prefixesInt() } is passed rather than just prefixesInt (The former takes the array as argument while the compiler passes the latter, which is a shorthand for self.prefixesInt, as a closure with the current value of self pre-bound – that self being the Array<Equatable> prefixes2 was called on).

HTH


To see the connection with the code you produced consider that:

<value>.method(<args>)

is just a shorthand for:

<type of value>.method(<value>)(<args>)

Which in this case means that:

$0.prefixesInt()

is a shorthand for:

Array<Int>.prefixesInt($0)()

which is what you produced but distributed between prefixes() (Array<Int>.prefixesInt) and encodeTransformDecode (transform(encode())()). By using the shorthand and passing the closure no changes to encodeTransformDecode are required.

CRD
  • 52,522
  • 5
  • 70
  • 86
0

With the help of useful nudges from @Carpsen90 and @CRD (thanks both!) and some dipping into the excellent book "obj Functional Swift" (No personal connection) I worked out a solution.

The type of an instance method is different from a static method as shown by:

extension Int {
  static func doubleStatic(_ x: Int) -> Int { return x * 2 }
  func doubleInstance() -> Int { return self * 2 }
}
print( type(of: Int.doubleStatic) )    // Prints: (Int) -> Int
print( type(of: Int.doubleInstance) )  // Prints: (Int) -> () -> Int

In the question the type of prefixesInt is in fact: (Array<Int>) -> () -> AnyIterator<[Int]>.
With this in mind we can rewrite encodeTransformDecode as follows:

extension Array where Element: Equatable {
  func encodeTransformDecode(transform: (Array<Int>) -> () -> AnyIterator<[Int]> ) -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return transform(encode())().lazy.map { $0.decode(self) }
  }
}

Secondly we need to tell the Compiler a bit more about the type of prefixesInt when we use it in prefixes2 so:

extension Array where Element: Equatable {
  func prefixes2() -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return encodeTransformDecode(transform: Array<Int>.prefixesInt)
  }
}

and now as required:

for prefix in ["A","B","C"].prefixes2() {
  print(prefix)
}

gives us:

["A"]
["A", "B"]
["A", "B", "C"]

and now very easily we can extend with other function very succinctly:

extension Array where Element == Int {
  func suffixesInt() -> AnyIterator<[Element]> {
    var length = 0
    return AnyIterator {
      guard length < self.count else { return nil }
      length += 1
      return Array(self.suffix(length))
    }
  }
}

extension Array where Element: Equatable {
  func suffixes2() -> LazyMapSequence<AnyIterator<[Int]>, [Element]> {
    return encodeTransformDecode(transform: Array<Int>.suffixesInt)
  }
}

for suffix in ["A","B","C"].suffixes2() {
  print(suffix)
}
Cortado-J
  • 2,035
  • 2
  • 20
  • 32
  • Arguably the phrase: `transform(encode())().lazy.map { $0.decode(self) }` is cryptic and might be better as: `let encoded = encode()` `let transformFunction = transform(encoded)` `let transformIterator = transformFunction()` `let lazyTransformIterator = transformIterator.lazy` `return lazyTransformIterator.map { $0.decode(self) }` – Cortado-J Nov 16 '18 at 20:07