How to apply an extension to only some specializations of a generic type?

Is there a way to apply an extension to a generic type so that it conforms to a protocol, when that extension will be valid only for certain specializations of the generic type?

For instance, consider this protocol which returns a dictionary counting the frequency of values contained by an instance conforming to the protocol:

// a type conforming to this protocol should return a dictionary
// which counts the frequencies of values contained by the type instance
protocol ConvertibleToFrequencyDictionary {
   typealias ItemType
   func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int>
}

Because the values being counted must serve as keys to the dictionary, the values must be of a type which conforms to Hashable. This is expressed by the type constraint on the definition of the generic dictionaryCountingFrequencies method. (I don't see any way to define the type constraint directly on the associated type, e.g., in the "typealias" declaration.)

Now consider this extension on Array, intended to make it conform to the protocol:

extension Array : ConvertibleToFrequencyDictionary {
  typealias ItemType=Element
  func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int> {
    var valueToCount = Dictionary<ItemType,Int>()
    for item in self {
      if let existingCount = valueToCount[item] {
        valueToCount.updateValue(value: existingCount + 1, forKey: item)
      } else {
        valueToCount.updateValue(value: 1, forKey: item)
      }
    }
    return valueToCount;
  }
}

This should return the frequencies of every distinct value appearing in Array. But of course, since these values must be hashable, this extension should only work when it is applied to an Array<T:Hashable>.

Yet this doesn't work for Array<Int>, even though Int is Hashable.

Why not? If you write an extension on a generic type, must that extension be able to work for every possible specialization of the generic type?

Answers


You can extend SequenceType instead of Array, this gives you more types this applies too and then you limit the type of the Element with where.

  • Array extensions need where Element : SomeProtocol
  • SequenceType extensions need where Generator.Element : SomeProtocol

The problem you were having was not that Int was not Hashable, it was that Item was not necessarily Hashable. A cast to ItemType also worked since it was always Hashable in this example.

Personally I would not use a generic for this. Since Element already covers everything you can store in an Array there is no need to make it type-fuzzy.

extension SequenceType where Generator.Element : Hashable {

    func dictionaryCountingFrequencies() -> Dictionary<Generator.Element,Int> {
        var valueToCount = Dictionary<Generator.Element,Int>()
        for item in self {

            if let existingCount = valueToCount[item] {
                valueToCount.updateValue(existingCount + 1, forKey: item)
            } else {
                valueToCount.updateValue(1, forKey: item)
            }
        }
        return valueToCount;
    }
}

let array = [1,2,3,4,5,1,1,1,2,3,3]

let freqDict = array.dictionaryCountingFrequencies()
// prints [5: 1, 2: 2, 3: 3, 1: 4, 4: 1]
freqDict[5]
// prints 1

Need Your Help

Cuda GPU optimization

cuda gpu acceleration

i have read that there were 100X acceleration on certain problems when you use NVIDIA GPU instead of CPU.

Update default version for easy install

python pypi

When I use easy_install &lt;package&gt; it would install the 1.0.0 version of my package, although, I already pushed the version 1.0.1 of this package to PyPi recently.