Property Wrappers + @inlinable

Problem

In the Property Wrapper proposal, the synthesised variable - in the form: _value - is deliberately private. There's a problem though, when wanting to use the property as @usableFromInline. I am aware that this works:

@propertyWrapper
public struct Wrapper<Value> {
    public var wrappedValue: Value

    @inlinable
    public init(wrappedValue: Value) { ... }
}

public struct Foo {
    @Wrapper 
    public var foo: Int

    @inlinable
    public init(foo: Int) { 
        self.foo = foo // ✅
    }
}

However, when a wrapper has more properties than just wrappedValue:

@propertyWrapper
public struct Wrapper<Value> {
    public var wrappedValue: Value
    public var shouldDoSomething: Bool

    @inlinable
    public init(wrappedValue: Value, shouldDoSomething: Bool) { ... }
}

public struct Foo {
    @Wrapper 
    public var foo: Int

    @inlinable
    public init(foo: Int) { 
        self._foo = Wrapper(
            wrappedValue: foo,
            shouldDoSomething: true
        ) 
        // ❌ Property _foo is private, hence, it can't
        //    be accessed from inlinable initialiser
    }
}

Solutions

  1. Change behaviour when marking public and internal wrapped properties @usableFromInline. So that this:

    @usableFromInline
    @Wrapper 
    public var foo: Int
    

    Becomes:

    @usableFromInline
    internal var _foo: Wrapper<Int> { ... }
    
    public var foo: Int {
        _foo
    }
    

    Rather than what I assume would be:

    private var _foo: Wrapper<Int> { ... }
    
    @usableFromInline  // ❌ Can't mark 
    // public property `foo` @usableFromInline  
    public var foo: Int {
        _foo
    }
    
  2. Introduce special @inlinable(underlyingProperty) attribute, so:

    public struct Foo {
        @inlinable(underlyingProperty)
        @Wrapper 
        public var foo: Int
    
        @inlinable
        public init(foo: Int) { 
            self._foo = Wrapper(
                wrappedValue: foo,
                shouldDoSomething: true
            ) 
            // ✅
        }
    }
    
  3. Other ideas from the community... Do you have any proposals?

My Opinion

I think that the first solution fits better in the direction of the language. It doesn't add further clutter to the language and what it does is mostly expected. When one sees the @usableFromInline and @Wrapper 'attributes' next to each other, their mind will point them to think that the underlying wrapper property is the one that is 'usedFromInline'.

On the other hand, an intermediate user seeing that a public property is marked @usableFromInline might be confused, if they're unaware that the proposed feature exists. Furthermore, @usableFromInline(underlyingProperty) makes it clear that what is '@usableFromInline' applies to the underlying property, not the exposed property foo.

Relevant

Thanks for readings and letting me know about your thoughts.

2 Likes
Terms of Service

Privacy Policy

Cookie Policy