Упорядоченный условный тип безопасности в массивах? - PullRequest
1 голос
/ 08 июля 2019

Есть ли способ добиться упорядоченной безопасности условного типа в массивах?

Например, если мне нужен DSL-подобный способ создания бургера, я мог бы сказать что-то вроде:

let burgerComponents = .burger(
    .buns(2),
    .beefPatty(1),
    .lettuce(),
    .mustard(),
    .mushrooms()
)

Где, если вы добавите тип булочки, вы не сможете добавить его снова к этомумассив.Если вы добавите beefPatty, вы не сможете добавить его снова и т. Д.

Наивным подходом будет создание Struct:

struct Burger {

    let buns: Int
    let patties: Int
    let lettuce: Bool
    let mustard: Bool
    let mushrooms: Bool

    init(
        buns: Int = 2,
        patties: Int = 1,
        lettuce: Bool = false,
        mustard: Bool = false,
        mushrooms: Bool = false) {
        self.buns = buns
        self.patties = patties
        self.lettuce = lettuce
        self.mustard = mustard
        self.mushrooms = mushrooms
    }
}

extension Burger {
     static func burger(
        buns: Int = 2,
        patties: Int = 1,
        lettuce: Bool = false,
        mustard: Bool = false,
        mushrooms: Bool = false) -> Burger {
        return Burger(buns: buns,
            patties: patties,
            lettuce: lettuce,
            mustard: mustard,
            mushrooms: mushrooms
        )
    }
}

let burger: Burger = .burger(buns: 2,
                             mushrooms: true)

Однако вы не можете расширять и добавлять дополнительные свойства кструктура бургера (майонез, острая горчица и т. д.).Решение Typed Tagless решает эту проблему, но, к сожалению, не дает упорядоченного соответствия.

Решение Struct работает, если вам нужно нерасширяемое решение, однако, поскольку я создаю расширяемый API, к которому другие участники смогут добавить дополнительные функции, решение struct, к сожалению, не отвечает этим требованиям.!

Изученные мною пути - это «Призрачные типы» и «Типизированные решения без тегов».

Типизированные без тегов

struct Burger {
   let buns: Int 
   let beefPatty: Int 
   let lettuce: Bool 
   let mustard: Bool 
   let mushrooms: Bool 
}

enum BurgerAttributes {
    case buns(Int)
    case beefPatty(Int) 
    case lettuce
    case mustard
    case mushrooms
}

protocol BurgerComponents {
    static func buns(_ numberOfBuns: Int) -> Self 
    static func beefPatty(_ numberOfPatties: Int) -> Self 
    static func lettuce() -> Self 
    static func mustard() -> Self 
    static func mushrooms() -> Self 
}

struct BurgerProcess {
    let attributes: BurgerAttributes
    init(_ attributes: BurgerAttributes) {
        self.attributes = attributes
    }
}

extension BurgerProcess: BurgerComponents {

    static func buns(_ numberOfBuns: Int) -> BurgerProcess {
        return BurgerProcess(.buns(numberOfBuns))
    }

    static func beefPatty(_ numberOfPatties: Int) -> BurgerProcess {
        return BurgerProcess(.beefPatty(numberOfPatties))
    }

    static func lettuce() -> BurgerProcess {
        return BurgerProcess(.lettuce)
    }

    static func mustard() -> BurgerProcess {
        return BurgerProcess(.mustard)
    }

    static func mushrooms() -> BurgerProcess {
        return BurgerProcess(.mushrooms)
    }
}

protocol BurgerComposer {
    static func burger(_ processes: BurgerProcess...) -> Self 
}

struct BurgerBuilder {
    typealias BurgerCompose = () -> Burger 
    let compose: BurgerCompose 
    init(compose: @escaping BurgerCompose) {
        self.compose = compose
    }
}

extension BurgerBuilder: BurgerComposer {

    static func burger(_ processes: BurgerProcess...) -> BurgerBuilder {
        return BurgerBuilder { 

            var _buns: Int = 0
            var _patties: Int = 0
            var _lettuce: Int = 0
            var _mustard: Int = 0
            var _mushrooms: Int = 0

            for process in processes {
                switch process.attributes {
                case .buns(let buns):
                    _buns = buns
                case .beefPatty(let patties):
                    _patties = patties
                case .lettuce:
                    _lettuce = lettuce
                case .mustard:
                    _mustard = true
                case .mushrooms:
                    _mushrooms = true
                }
            }

            return Burger(
                buns: _buns,
                beefPatty: _patties,
                lettuce: _lettuce,
                mustard: _mustard,
                mushrooms: _mushrooms
            )
        }
    }

}

Теперь вы сможете создавать бургер в DSL-стиле.fashion:

let builder: BurgerBuilder = .burger(   
    .buns(2),
    .beefPatty(1),
    .lettuce(),
    .mustard(),
    .mushrooms()
)

let burger: Burger = builder.compose()
// Output: Burger(buns: 2, beefPatty: 1, lettuce: true, mustard: true, mushrooms: true)

Таким образом, решение Typed Tagless удовлетворяет некоторым требованиям, однако, если вы хотите иметь условное упорядочение, это невозможно с решением Typed Tagless.

С Typed Tagless вы можете определить неограниченное количество атрибутов:

let builder = BurgerBuilder = .burger(
    .buns(2),
    .buns(0),
    .buns(10),
    .mustard()
)

Там нет безопасности для внешнего пользователя API.

Есть ли способ достичьупорядоченный тип соответствия в следующем формате?

let burgerComponents = .burger(
    .buns(2),
    .beefPatty(1),
    .lettuce(),
    .mustard(),
    .mushrooms()
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...