Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is generic required when all types are already defined?

Tags:

generics

swift

The exercise was to write my own map() function over Collection (without using any functional primitives, such as reduce()). It should handle a case such as this:

func square(_ input: Int) -> Int {
            return input * input
        }
let result = input.accumulate(square) // [1,2,3] =>  [1,4,9]

My first attempt was:

extension Collection {
    func accumulate(_ transform: (Element) -> Element) -> [Element] {

        var array: [Element] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    

This works fine in a playground, but fails to build against the tests, giving an error:

Value of type '[Int]' has no member 'accumulate'

The solution is to genericize the accumulate method:

extension Collection {
    func accumulate<T>(_ transform: (Element) -> T) -> [T] {

        var array: [T] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    

I recognize that the generic version is less restrictive (doesn't require the transform to return same type), but given that the tests don't require this generality, why does the compiler?

Out of curiousity I tried:

extension Collection {
    func accumulate<Element>(_ transform: (Element) -> Element) -> [Element] {

        var array: [Element] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    

which throws the fascinating build error: '(Self.Element) -> Element' is not convertible to '(Element) -> Element' at the append() statement.

So the compiler (of course) knows that the first Element is Self.Element, but doesn't treat the other Element type as the same. Why?


UPDATE:

Based on the answers, it appears that the rejection of the first version was a compiler bug, fixed in XCode 9.2 (I'm on 9.1).

But still I wondered whether in

func accumulate(_ transform: (Element) -> Element) -> [Element]

it would see two types (Self.Element and Element) or recognize that they're the same.

So I did this test:

let arr = [1,2,3]
arr.accumulate {
    return String(describing: $0)
}

Sure enough, got the expected error: error: cannot convert value of type 'String' to closure result type 'Int'

So the correct answer is: the compiler will treat references to Element as the same, as long as there isn't a generic type that overloads the name.

Oddly, though, this succeeds:

[1,2,3].accumulate {
    return String(describing: $0)
}

PS. Thanks to everyone for your input! The bounty was auto-awarded.

like image 483
vortek Avatar asked Dec 06 '17 01:12

vortek


People also ask

Why is generic programming needed?

Code that uses generics has many benefits over non-generic code: Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.

What is the benefit of having a generic collection?

Better performance. Generic collection types generally perform better for storing and manipulating value types because there is no need to box the value types. Generic delegates enable type-safe callbacks without the need to create multiple delegate classes.

Why primitive data types are not allowed in Java generics?

So, anything that is used as generics has to be convertable to Object (in this example get(0) returns an Object ), and the primitive types aren't. So they can't be used in generics.

What is the purpose of using generic in C #?

Generics allow you to define the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.


1 Answers

The original build error was a compiler error. In fact, the compiler will recognize that all instances of Element are the same, as long Element hasn't been overloaded as a generic type on the function.

like image 158
vortek Avatar answered Oct 02 '22 08:10

vortek