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?