What is wrong with wrappers?

I wanted to write some code for testing. My intention was to use property wrappers, to define some properties for types and variables. I decided to choose these: Unique (inspired by uniqueness types), Constrained (inspired by type refinement) and Linear inspired by linear types.

You can try this code

@propertyWrapper
public struct Unique<T> {
    internal enum State {
        case present(T)
        case absent
    }
    internal var value: State
    public var projectedValue: Unique<T> { return self }
    public var isEmpty: Bool {
        switch self.value {
            case .absent:
            return true
            default:
            return false
        }
    }
    public var wrappedValue: T {
        mutating get {
            switch self.value {
                case .present(let val):
                    self.value = .absent
                    return val
                default:
                    fatalError("value has already been used at this time and has been moved out to a another place.")
            }
        }
        set {
            value = .present(newValue)
        }
    }
    public mutating func fill (with newValue: T) {
        self.value = .present(newValue)
    }
    public init(wrappedValue: T) {
        self.value = .present(wrappedValue)
    }
}
public typealias Predicate<T> = (T) -> Bool
@propertyWrapper
public final class Constrained<T> {
    
    internal var constraints: Array<Predicate<T>>
    internal var value: T
    public var projectedValue: Constrained<T> { return self }
    public var wrappedValue: T {
        get { return value  }
        set {
            #if debug
            for predicate in constraints {
                if predicate(newValue) == false {
                    fatalError("Predicate \(predicate) of constrained value '\(self)' was not satisfied with value \(newValue)")
                }
            }
            #endif
            value = newValue
        }
    }
    public init(wrappedValue: T, by constraints: Array<Predicate<T>> = []) {
        self.constraints = constraints
        self.value = wrappedValue
        self.wrappedValue = value
    }
    public init(wrappedValue: T, by constraint: @escaping Predicate<T>) {
        self.constraints = []
        self.constraints.append(constraint)
        self.value = wrappedValue
        self.wrappedValue = value
    }
    public func addNewConstraint(_ constraint: @escaping Predicate<T>) {
        self.constraints.append(constraint)
    }
}
@propertyWrapper
public struct Linear<T> {
    public enum Strictness: Equatable { case exact, inexact }
    internal var value: T
    private var typeSequence: ArraySlice<(T) throws -> Bool>
    private let strict: Bool
    public var wrappedValue: T {
        get {
            return value
        }
        set {
            #if debug
            do {
                if typeSequence.first?(newValue) ?? false {
                    value = newValue
                    typeSequence = typeSequence.dropFirst()
                } else {
                    if self.strict && typeSequence.isEmpty {
                        fatalError("Cannot assign new value to this variable. Linearity is exhausted")
                    } else {
                        value = newValue
                    }
                   
                }
            } catch is String {
                fatalError("Failed to assign value because \(error)")
            }
            #else
            value = newValue
            #endif
        }
    }
    public init(wrappedValue: T, continuous: Bool = false, _ typeSequence: Array<(T) throws -> Bool>) {
        self.typeSequence = typeSequence[...]
        value = wrappedValue
        self.strict = continuous
        self.wrappedValue = wrappedValue
    }
}
public struct Skp {
    @Linear([
        {$0 == 5},
        {$0 != 5}])
    //@Constrained(by: {$0 % 2 == 0})
    public var a: Int = 5
    
    @Linear([
        {$0.count > 0},
        {$0.contains("J")}])
    public var test: String = "Name"
    
    @Linear([
        {type(of: $0) == String.self},
        {type(of: $0) == Int.self}])
    public var age: Any = "5"
    
    static public let tuple = ("hello", 5, 0.0)
    @Linear([
        {type(of: $0) == (() -> String).self},
        {type(of: $0) == (() -> Int).self},
        {type(of: $0) == (() -> Double).self}])
    public var iteratorForHomogenousList: Any = { tuple.0 }
    
    @Constrained(by: {$0.count > 0})
    public var a1: String = "Ahahahah"
    
    @Unique
    public var a2: String = "catch me if you can"
}
var testing123 = Skp()
testing123.a = 0
testing123.test.append("J")
testing123.age = Int(testing123.age as! String)
print(testing123.a2)
//print(testing123.a2) //cant be used twice
print((testing123.iteratorForHomogenousList as! () -> String)())
testing123.iteratorForHomogenousList = { Skp.tuple.1 }
print((testing123.iteratorForHomogenousList as! () -> Int)())
testing123.iteratorForHomogenousList = { Skp.tuple.2 }
print((testing123.iteratorForHomogenousList as! () -> Double)())

@Linear([        //each mutation must satisfy consecutive closure
  {type(of: $0) == JSON.self},
  {type(of: $0) == String.self}])
public var name: Any = ... json instance assignment ...
name = (name as! JSON)["name"] as! String

@Unique         //can be used only once
var secretCode: String = "@d3Eag67Yu"

@Constrained(by: {$0 > 0 && $0 < 200}) //any value must satisfy this 
var age: Int = 19

Each one of these works by itself, but when they are combined I get lovely ...
error: multiple property wrappers are not supported or error: the compiler is unable to type-check this expression in reasonable time;. In 2020th.
I also can't use them inside functions because error: property wrappers are not yet supported on local properties.
By the way, why wrapper cannot be a part of a type? Why can't I write neither

protocol HasHumanlikeLifespan {
    @Constrained(by: {$0 > 0 && $0 < 150})
    var age: Int { get }
}

nor something like this

protocol Trainable { var skills: Set<Skill> { get set } }
func produceNaturalBornKiller () -> 
@Linear(continuous: true, [{$0.skills.isEmpty}, {$0.skills.containsAny {$0 is KillCommand}}]) 
Trainable { [Dog(), Man()].randomElement! }
//for example

When all of this gonna be fixed? :expressionless:

Nested property wrapper is simply not supported at the moment. The proposal did outline its expected behaviour, though. I'm not sure if it simply falls off the checklist, or there's a bug that prevents its inclusion. Hopefully, someone can shed light on that. See edit below.

Using property wrappers with local variables is not part of the original proposal and has not been formally proposed yet. Same with property wrapper on protocol requirements and function arguments.

When someone implements it. :ok_hand:

Edit:

This works fine on Xcode 11.6 (Swift 5.2.4):

struct Test {
    @Unique @Constrained(by: { $0.count > 0 }) var a: String = "Test"
}
Terms of Service

Privacy Policy

Cookie Policy