Обработка динамических выходных данных в функциях Swift без ущерба для безопасности типов (любая)

У меня есть базовая пользовательская структура в Swift, где использование Any для типов ввода далеко не идеально из-за статической типизации языка, которая может обойти проверку типов компилятором и привести к ошибкам и потере информации о типах.

Однако я столкнулся с необходимостью вернуть Any из таких функций, как vectorAssoc, особенно в случаях, когда тип возвращаемого значения может быть кортежем или логическим значением. Вот пример функций Vector и vectorAssoc:

struct Vector<Element>: ExpressibleByArrayLiteral {
    private var storage: [Element] = []
    
    public init(arrayLiteral elements: Element...) {
        storage = elements
    }
    
    func count() -> Int {
        return storage.count
    }
    
    subscript(index: Int) -> Element {
        return storage[index]
    }
}

func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> Any {
    func helper(_ i: Int) -> Any {
        if i >= vec.count() {
            return false
        } else {
            let elem = vec[i]
            
            if elem.0 == v {
                return elem
            } else {
                return helper(i + 1)
            }
        }
    }
    
    return helper(0)
}

let vec: Vector = [(2, 1), (3, 1), (4, 1), (5, 1)]

// Example usage
let a = vectorAssoc(4, vec) // prints (4, 1)
let b = vectorAssoc(12, vec) // prints false

Поскольку выходные данные vectorAssoc могут использоваться в качестве входных данных для другой функции, я ищу предложения о том, как более эффективно обрабатывать возвращаемый тип Any. В идеале я ищу способ указать, что тип возвращаемого значения может быть только одним из двух конкретных типов (кортеж или логическое значение), не прибегая к пользовательским типам или дополнительным параметрам.

Если существует способ преобразования такого Any в примитивный тип, использующий преимущества функций безопасности типов Swift, я был бы очень признателен за любые идеи или улучшения этой настройки.


Итак, очень простое объяснение нового вопроса:

Any в данном случае — это ТОЛЬКО 2 типа (кортеж и логический), его не может быть больше двух. Если бы был способ вернуть Any (только два конкретных типа) и каким-то образом Any стал бы примитивным типом (одним из этих двух), который мог бы воспользоваться преимуществами безопасности типов. Есть ли способ заставить это работать?


Редактировать:

Но было бы неплохо иметь новый тип, что-то вроде:

func foo(_ param: in Type) -> out Type { ... }

Этот тип похож на Any в том, что он может представлять несколько типов, но в этом случае он будет ограничен только двумя типами: кортежем и логическим значением. Вам необходимо указать как вход, так и выход, поскольку этот тип может быть возвращен из одной функции и передан в другую.

Другой подход может быть:

func foo(_ param: Int) -> out Type { ... }

Цель состоит в том, чтобы избежать переноса и развертывания значений, использования дополнительных опций или других сложностей. В идеале вы могли бы напрямую получить из него примитивный тип, упрощая обработку нескольких возможных типов возвращаемых значений.

Таким образом (-> out Type) он может быть одним примитивным типом при передаче в другую функцию. Тип будет работать по тем же принципам, что и примитивные типы, но если вы все же хотите, чтобы тип имел несколько типов, используйте (_ param: in Type), но только два типа, а не Any.

Хорошее объяснение здесь: Обработка динамических выходных данных в функциях Swift без ущерба для безопасности типов

Я добавил базовое объяснение, потому что первый вопрос был закрыт. Я не знаю, какое еще объяснение понадобится этому вопросу. Подход может быть чем-то похожим на python Int | Bool или, может быть, Any((Int, Int) Bool).

Adrian 29.06.2024 16:46

Изначально я использовал -> (Int, Int)? с nil, но я изменил его на текущую форму, потому что она определенно лучше и устраняет необходимость в дополнительных опциях. Теперь я перешел от работы с необязательным «уродством» к работе с любым «уродством».

Adrian 29.06.2024 16:52

Вы говорите о двух типах, но Bool — это просто жестко закодированное значение false, поэтому в основном это значение или всегда единственное постоянное значение false, которое возвращается. Так почему nil настолько хуже, чем false, я не понимаю. Вы могли бы избежать этого, выполнив функцию throw, но я предполагаю, что это тоже в некотором смысле некрасиво.

Joakim Danielson 29.06.2024 17:23

Вот хорошее объяснение этому stackoverflow.com/questions/78685976/…

Adrian 29.06.2024 17:39
Optional определенно правильный путь сюда. Этот вариант использования — именно то, для чего он был разработан.
JeremyP 29.06.2024 21:14

Кроме того, я не совсем понимаю, почему вы не используете Array просто для Vector.

JeremyP 29.06.2024 21:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Правильно ли я понимаю, что логический тип возвращаемого значения предназначен только для обозначения ошибки? Если да, то почему бы не выдать ошибку явно?

func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) throws -> (Int, Int)

do {
    print(try vectorAssoc(4, vec))
} catch {
    print(false)
}

Если речь идет не об ошибках, а о возврате одной опции из ограниченного набора опций, в Swift для этой цели есть перечисления:

enum ReturnOption {
    case bool(value: Bool)
    case int(value: Int)
}

func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> ReturnOption

switch  vectorAssoc(4, vec) {
    case .bool(let value),
         .int(let value):
        print(value)
}

Если вам нужны только два состояния: значение и отсутствующее значение, в Swift для этого есть Optional:

func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> (Int, Int)?

if let result = vectorAssoc(4, vec) {
    print(result)
} else {
    print(false)
}

Я бы сказал, что в этом случае возвращение необязательного параметра является правильным ответом.

JeremyP 29.06.2024 21:10

Может быть, необязательно, что все делает автоматически.

Adrian 30.06.2024 12:56

Другие вопросы по теме

Использование протокола как типа должно быть записано с любыми (целями обучения)
Какой смысл имеет оператор стрелки в Haskell в функциях, принимающих более одного параметра?
Как исправить столбец с числовыми значениями, который воспринимается как строковое поле из-за пустых строк в фрейме данных Pandas?
Как создать собственный тип для анализа [u8;32] из json, содержащего шестнадцатеричную строку, в Rust
Ограничить тип возвращаемого значения подтипом этого
Компиляция карты времени имени строки типа в типы в C++
Вычисление чисел, для которых требуется тип данных размером более 16 байт в C++
Введите 'строка | логическое значение не может быть назначено для ввода «никогда» в машинописном тексте
Почему тип int меняет размер в зависимости от архитектуры процессора, а другие типы — нет?
Определение макроса и использование типов данных для поиска абсолютного значения