Swift: вызов функции-преобразователя значений Enum как функции первого класса - PullRequest
1 голос
/ 28 марта 2020
enum SolarSystemPlanet: String, CaseIterable {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune

    func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
        value.rawValue
    }
}

С перечисленным выше перечислением один из способов получить массив имен pl anet - это вызвать

SolarSystemPlanet.allCases.map { $0.rawValue }

Но Swift поддерживает функции первого класса, рассматривая функции как "граждан первого класса" ", что позволяет нам вызывать функции, как и любой другой объект или значение.

Так что было бы неплохо получить массив имен этим

SolarSystemPlanet.allCases.map(.toRawValue)

Однако, похоже, что Компилятору нужно больше контекста. Он не может определить тип map во время компиляции, поэтому я сделал

SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)

Компилятор перестает жаловаться, но я не получаю массив String. В строке выше возвращается значение типа [(SolarSystemPlanet) -> String]

Если я распечатал вышеупомянутое, вместо получения

["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]

я получил

[(Function), (Function), (Function), (Function), (Function), (Function), (Function), (Function)]

Если я принудительно тип возвращаемого значения [String], например,

var planets: [String] = SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)

Xcode будет жаловаться, что [(SolarSystemPlanet) -> String] не может быть преобразовано в [String]

Возможно ли все-таки достичь того, что я пытаюсь сделать? Я что-то упускаю или делаю что-то не так?

И если это невозможно, я также буду признателен за некоторые объяснения причин.

Спасибо, что потратили время на чтение моего вопроса!


Edit Спасибо за ответ @ sweeper .

Для тех, кто заинтересован, я пошел немного дальше, чтобы убедиться, что каждое перечисление String имеет toRawValue

extension RawRepresentable where RawValue == String {
    static func toRawValue(_ value: Self) -> PlanetName {
        value.rawValue
    }
}

Примечание: это Swift 5.1.3

1 Ответ

1 голос
/ 28 марта 2020

Обратите внимание, что toRawValue не обязательно должен быть методом экземпляра. Это может быть stati c:

static func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
    value.rawValue
}

Теперь вы можете использовать SolarSystemPlanet.toRawValue в качестве аргумента для map.

В качестве альтернативы, в этом случае вы также можете использовать keypath из \.rawValue в качестве аргумента для map:

SolarSystemPlanet.allCases.map(\.rawValue)

Это новая функция Swift 5.2.

EDIT: Объяснение, почему методы экземпляра don не работает

В Swift при доступе из контекста stati c метод экземпляра для типа T с подписью (U) -> R становится методом stati c с подписью (T) -> ((U) -> R). Методы экземпляра нуждаются в экземпляре включающего типа для вызова, верно? Поэтому, когда вы передаете ему T, он возвращает вам исходную функцию экземпляра (U) -> R назад.

Следовательно, тип non-stati c SolarSystemPlanet.toRawValue равен

(SolarSystemPlanet) -> ((SolarSystemPlanet) -> String)

Это объясняет, почему после применения map массив становится [(SolarSystemPlanet) -> String].

...