[Pitch] Scoped @available

Hi evolution community,

I would like to propose "Scoped @available" attribute.

What I want to achieve is to declare something that is publicly
unavailable, but still usable from narrower scope. A concrete example is
IndexableBase in the standard library:

public/core/Collection.swift#L18-L20
Workaround for this problem in stdlib is to use typealias to underscored
declaration. However, we can't use this technique in our module because
underscored declarations are still visible and usable from outside.

As a solution, I propose to add "access" parameter to @available attribute,
which limits the effect of @available attribute to the specified value or
outer.

···

---
// Module: Library

/// This protocol is available for internal, but deprecated for public.
@available(*, deprecated, access: public, message: "it will be removed in
future")
public protocol OldProtocol {
    /* ... */
}

public Foo: OldProtocol { // No diagnostics
}

---
// Module: main

import Library

public Bar: OldProtocol { // warning: 'OldProtocol' is deprecated: it will
be removed in future
}

---

I think this is useful when you want to stop exposing declarations, but
want to keep using them internally.

More examples:

// is `open`, going to be `filerprivate`
@available(*, deprecated, access: internal)
open class Foo {}

// was `internal`, now `private`
@available(*, unavailable, access: fileprivate)
var value: Int

// No effect (invisible from public anyway): emit a warning
@available(*, unavailable, access: public)
internal struct Foo {}

What do you think?

This would be very useful, but the spelling needs dramatic improvement.

"Available unavailable" is already challenging to read, but at least it is
learnable with time. The example where "@available(*, deprecated, access:
internal) open" means "fileprivate" is entirely unreasonable.

···

On Sun, Jul 9, 2017 at 22:40 rintaro ishizaki via swift-evolution < swift-evolution@swift.org> wrote:

Hi evolution community,

I would like to propose "Scoped @available" attribute.

What I want to achieve is to declare something that is publicly
unavailable, but still usable from narrower scope. A concrete example is
IndexableBase in the standard library:

https://github.com/apple/swift/blob/master/stdlib/public/core/Collection.swift#L18-L20
Workaround for this problem in stdlib is to use typealias to underscored
declaration. However, we can't use this technique in our module because
underscored declarations are still visible and usable from outside.

As a solution, I propose to add "access" parameter to @available
attribute, which limits the effect of @available attribute to the
specified value or outer.

---
// Module: Library

/// This protocol is available for internal, but deprecated for public.
@available(*, deprecated, access: public, message: "it will be removed in
future")
public protocol OldProtocol {
    /* ... */
}

public Foo: OldProtocol { // No diagnostics
}

---
// Module: main

import Library

public Bar: OldProtocol { // warning: 'OldProtocol' is deprecated: it will
be removed in future
}

---

I think this is useful when you want to stop exposing declarations, but
want to keep using them internally.

More examples:

// is `open`, going to be `filerprivate`
@available(*, deprecated, access: internal)
open class Foo {}

// was `internal`, now `private`
@available(*, unavailable, access: fileprivate)
var value: Int

// No effect (invisible from public anyway): emit a warning
@available(*, unavailable, access: public)
internal struct Foo {}

What do you think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This would be very useful, but the spelling needs dramatic improvement.

"Available unavailable" is already challenging to read, but at least it is
learnable with time. The example where "@available(*, deprecated, access:
internal) open" means "fileprivate" is entirely unreasonable.

I agree, but I couldn't come up with a better spelling.

// "deprecated" if the access is from "outside" of "fileprivate" scope.
@available(*, deprecated, outside: fileprivate) open

Hmm.. :thinking:

Any suggestions will be greatly appreciated!

···

2017-07-10 14:05 GMT+09:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

On Sun, Jul 9, 2017 at 22:40 rintaro ishizaki via swift-evolution < > swift-evolution@swift.org> wrote:

Hi evolution community,

I would like to propose "Scoped @available" attribute.

What I want to achieve is to declare something that is publicly
unavailable, but still usable from narrower scope. A concrete example is
IndexableBase in the standard library:
https://github.com/apple/swift/blob/master/stdlib/public/cor
e/Collection.swift#L18-L20
Workaround for this problem in stdlib is to use typealias to underscored
declaration. However, we can't use this technique in our module because
underscored declarations are still visible and usable from outside.

As a solution, I propose to add "access" parameter to @available
attribute, which limits the effect of @available attribute to the
specified value or outer.

---
// Module: Library

/// This protocol is available for internal, but deprecated for public.
@available(*, deprecated, access: public, message: "it will be removed in
future")
public protocol OldProtocol {
    /* ... */
}

public Foo: OldProtocol { // No diagnostics
}

---
// Module: main

import Library

public Bar: OldProtocol { // warning: 'OldProtocol' is deprecated: it
will be removed in future
}

---

I think this is useful when you want to stop exposing declarations, but
want to keep using them internally.

More examples:

// is `open`, going to be `filerprivate`
@available(*, deprecated, access: internal)
open class Foo {}

// was `internal`, now `private`
@available(*, unavailable, access: fileprivate)
var value: Int

// No effect (invisible from public anyway): emit a warning
@available(*, unavailable, access: public)
internal struct Foo {}

What do you think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This is something I’m dealing with as a framework author as well.

I’m stumped coming up with anything better than your latter idea of “outside” even though I’m not really a fan.

Either way, I think that deprecating outside makes some sense from a framework developer’s perspective. You may need to signal an API is “not to be relied on” despite knowing at least temporarily you will need to keep using it, but it’s not recommended for outside consumption for various reasons.. I also think there’s a lot of general work towards handling deprecation warnings cleanly in Swift that hasn’t been addressed, but that’s really for another topic.

- Rod

···

On 10 Jul 2017, at 5:39 pm, rintaro ishizaki via swift-evolution <swift-evolution@swift.org> wrote:

2017-07-10 14:05 GMT+09:00 Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>>:
This would be very useful, but the spelling needs dramatic improvement.

"Available unavailable" is already challenging to read, but at least it is learnable with time. The example where "@available(*, deprecated, access: internal) open" means "fileprivate" is entirely unreasonable.

I agree, but I couldn't come up with a better spelling.

// "deprecated" if the access is from "outside" of "fileprivate" scope.
@available(*, deprecated, outside: fileprivate) open

Hmm.. :thinking:

Any suggestions will be greatly appreciated!

On Sun, Jul 9, 2017 at 22:40 rintaro ishizaki via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi evolution community,

I would like to propose "Scoped @available" attribute.

What I want to achieve is to declare something that is publicly unavailable, but still usable from narrower scope. A concrete example is IndexableBase in the standard library:
https://github.com/apple/swift/blob/master/stdlib/public/core/Collection.swift#L18-L20
Workaround for this problem in stdlib is to use typealias to underscored declaration. However, we can't use this technique in our module because underscored declarations are still visible and usable from outside.

As a solution, I propose to add "access" parameter to @available attribute, which limits the effect of @available attribute to the specified value or outer.

---
// Module: Library

/// This protocol is available for internal, but deprecated for public.
@available(*, deprecated, access: public, message: "it will be removed in future")
public protocol OldProtocol {
    /* ... */
}

public Foo: OldProtocol { // No diagnostics
}

---
// Module: main

import Library

public Bar: OldProtocol { // warning: 'OldProtocol' is deprecated: it will be removed in future
}

---

I think this is useful when you want to stop exposing declarations, but want to keep using them internally.

More examples:

// is `open`, going to be `filerprivate`
@available(*, deprecated, access: internal)
open class Foo {}

// was `internal`, now `private`
@available(*, unavailable, access: fileprivate)
var value: Int

// No effect (invisible from public anyway): emit a warning
@available(*, unavailable, access: public)
internal struct Foo {}

What do you think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution