Pull to refresh

Comments 3

Важно помнить, что подобные решения — это злоупотребление системой типов. Своего рода хак. Любой такой хак представляет собой неочевидный подход для решения критически важных проблем, жертвуя самодокументированием кода.
Возможно в каких-то ситуациях такой подход и может выполнить роль предохранителя, но главным образом фантомные типы используют для разгрузки рантайма от лишних проверок, делегируя эту задачу компилятору.

Кроме этого, в статье приведены неудачные примеры, демонстрирующие отсутствие понимания культуры программирования на Swift.
1) Пример с User и Product. Для сравнения двух бизнес объектов не требуется в явном виде сравнивать их идентификаторы. Достаточно лишь унаследовать протокол Equatable и сравнивать объекты напрямую. При желании можно переопределять дефолтный алгоритм сравнения (если не требуется сравнение по всем полям структуры).
struct User {
  let id: UUID
}

struct Product {
  let id: UUID
}

extension User: Equatable {
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }
}

extension Product: Equatable {
    static func == (lhs: Product, rhs: Product) -> Bool {
        return lhs.id == rhs.id
    }
}

let product = Product(id: UUID())
let user = User(id: UUID())

user == product

Проверка произойдет также на уровне компилятора и не даст собрать неверный код.

2) Пример с fetch. Для методов получения бизнес объектов из хранилища, также вместо идентификатора обычно передаётся объект целиком, либо используется специальный класс для задания параметров поиска NSPredicate.
func fetch(_ product: Product) -> Product? {
    // return product
}

fetch(user)

Проверка тоже на уровне компилятора — никаких проблем.

3) Пример с HealthKit. Тут всё просто. Проблема из ничего. Для проверки возможности скастить единицу измерения у этого же класса есть специальный метод is(compatibleWith:) developer.apple.com/documentation/healthkit/hkquantity/1615508-is
let average = statistics?.averageQuantity()
var mass = 0.0
if let qty = average,
   qty.is(compatibleWith: .meter())
{
  mass = qty.doubleValue(for: .meter())
}

Это конечно райнтайм, но зато без замусоривания кода лишними абстракциями.

Поэтому к такому способу жонглирования типами нужно подходить очень избирательно.

И кстати Generics во всем цивилизованном мире зовутся обобщениями (обобщенными типами), а не универсальными типами.
UFO just landed and posted this here
К сожалению нет. Введение фантомного типа не допускает динамического изменения свойств родительского типа. В этом и весь смысл. У нас есть два структурно одинаковых объекта, но нам нужно разделить их семантически. Тогда мы декларируем обобщение, которое никак не используется и получаем уже два несовместимых типа.
Sign up to leave a comment.