# Constrain protocol usage outside the module

**URL:** <https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832>\
**Category:** Pitches\
**Created:** [January 25, 2018, 9:03am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832 "2018-01-25T09:03:18Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![tali](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tali/32/11346_2.png) [@tali](https://forums.swift.org/u/tali)\
**Post date:** [January 27, 2018, 7:05am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/21 "2018-01-27T07:05:00Z")

</div>

Is switching over types really something we want to encourage? I‘ve always tried to avoid it.  
If you want to abstract over these types with a protocol, I think it would be better to create a protocol with appropriate methods.  
Maybe you are reluctant to expose these methods to other modules? That could be something which is worth solving by allowing private requirements.

---

<div class="post-metadata">

**Author:** ![hashemi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hashemi/32/9508_2.png) [@hashemi](https://forums.swift.org/u/hashemi)\
**Post date:** [January 27, 2018, 7:40am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/22 "2018-01-27T07:40:31Z")

</div>

If we look at protocols as an encapsulation tool then switching over types doesn’t feel right. If we look at them as just another sum type, like enums but different, then switching fits nicely.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 7:45am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/23 "2018-01-27T07:45:35Z")

</div>

Implementation that gets called by the subscript with variadic `Int | String` parameter in the examples below.

```swift
extension Document {
    public subscript(valueAt position: Int) -> Value { ... }
    public subscript(key: String) -> Value? { ... }
    
    subscript(firstKey: String, parameters: [SubscriptParameter]) -> Value? {
        get {
            var currentValue: Value? = self[firstKey]
            for parameter in parameters {
                guard let value = currentValue else { return nil }
                switch value {
                case .document(let document):
                    guard case .string(let key) = parameter else { return nil }
                    currentValue = document[key]
                case .array(let array):
                    guard case .integer(let position) = parameter else { return nil }
                    currentValue = array[position]
                default:
                    return nil
                }
            }
            return currentValue
        }
        set { ... }
    }
}

```

Current solution:

```swift
// Tranforms [Int, String, String, Int] into:

[
  .integer(Int),
  .string(String),
  .string(String),
  .integer(Int)
]

// just to be able to extract the associated types later.
// This is a lot of extra overhead.

```

```swift
extension Document {
    public enum SubscriptParameter {
        case string(String)
        case integer(Int)
    }
}

// From the definition of this thread this is a `non-frozen` and `open` protocol.
public protocol SubscriptParameterType {
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
    public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? {
        get { return self[firstKey, parameters.map { $0.parameter }] }
        set { self[firstKey, parameters.map { $0.parameter }] = newValue }
    }
}

```

Ideal solution using `@frozen` and `anonymous cases` with an enum:

```swift
extension Document {
    @frozen public enum KeyOrIndex {
        case (String) // anonymous case
        case (Int) // anonymous case
    }
}

extension Document {
    public subscript(firstKey: String, parameters: KeyOrIndex...) -> Value? {
        get { return self[firstKey, parameters] }
        set { self[firstKey, parameters] = newValue }
    }
}

```

A solution using `@frozen @closed public` aka `@frozen public` (but not open) protocol.

```swift
@frozen @closed
public protocol KeyOrIndex /* : where Self == String || Self == Int */ {}

/* Ideally it should be spelled as:
 * @frozen public protocol KeyOrIndex /* : where Self == String || Self == Int */ {}
 * In both versions I included the where clause which forces a concrete conforming type.
 */
extension Int : KeyOrIndex {}
extension String : KeyOrIndex {}

extension Document {
    public subscript(firstKey: String, parameters: KeyOrIndex...) -> Value? {
        get { return self[firstKey, parameters] }
        set { self[firstKey, parameters] = newValue }
    }
}

```

In the end the user of the library can do very convenient things like `aDocument["first_key", 2, "key", "other_key"]` to travers nested documents and arrays.

The user is simply not meant to conform to the current `SubscriptParameterType` because it will probably fail (but not crash). I as the library designer want to prevent such things by also providing guarantees enforced by the compiler.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 8:12am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/24 "2018-01-27T08:12:36Z")

</div>

Furthermore can someone elaborate why it is not possible for Swift to change syntax in different modes while rolling out a feature like this in stages? I think I've seen @beccadax mentioning this kind of process in quite few different topics by now.

Here is **one** example:

- In Swift 5 we introduce `@closed public protocol` and `open protocol` additionally to `public protocol`, but `public protocol` without `@closed` will emit a warning and provide a fix-it to `open protocol`.
- In Swift 6 mode this becomes an error and we'll have `@closed public protocol` and `open protocol` only.
- In Swift 7 mode `@closed` can be omitted and we'll finally have `public protocol` and `open protocol`.
- In Swift 7.X we also warn that `@closed` will be deprecated in the next major release.
- In Swift 8 mode `@closed` is an error because it's deprecated, now only `open protocol` and `public protocol` are allowed.

* * *

| Swift 4 | Swift 5 | Swift 6 | Swift 7 | Swift 7.X | Swift 8 |
| --- | --- | --- | --- | --- | --- |
| `public protocol` | (warning) `public protocol` (fix-it to) `open protocol` | (error when `public protocol`) `open protocol` | `open protocol` | Same as in Swift 7 | Same as in Swift 7.X |
| - | `@closed public protocol` | `@closed public protocol` | (warning `@closed` can be omitted) `public protocol` | Same as in Swift 7 but with a deprecation warning of `@closed` | (error when `@closed`, because it's deprecated now) `public protocol` |

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 8:51am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/25 "2018-01-27T08:51:09Z")

</div>

I'm sorry, I can't decipher this. It's a solid block of code. Can you describe, in words, the motivating problem?

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 8:54am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/26 "2018-01-27T08:54:17Z")

</div>

> [@DevAndArtist](#):
>
> Furthermore can someone elaborate why it is not possible for Swift to change syntax in different modes while rolling out a feature like this in stages? I think I’ve seen @brentdax mentioning this kind of process in quite few different topics by now.

The problem is that it changes the meaning of `public protocol`. Whether you do this over one release of Swift or five releases of Swift (which is arguably much worse), you're making a source-breaking change. This requires a high level of justification of active harm.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 8:55am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/27 "2018-01-27T08:55:08Z")

</div>

Sorry but I already did. Just look closer at the examples. The first block is just a block which shows where the `Int` and `String` are extracted. Then there are three examples of a possible implementation:

- Current implementation (with overhead and no possible constraints at all)
- Ideal solution using enums, but with a feature we probably never get
- A solution using protocols to be able to switch over the `Self` type in a constrained but safe manner

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 8:59am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/28 "2018-01-27T08:59:13Z")

</div>

Can you elaborate the source breaking change here, because I don't see it. The staged example release does not have a source breaking change from my point of view, it just deprecates specific syntax over time, which should be allowed, or how can you justify the move from `private` to a `fileprivate`-like access modifier for Swift 4?

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:01am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/29 "2018-01-27T09:01:30Z")

</div>

> [@DevAndArtist](#):
>
> Sorry but I already did. Just look closer at the examples. The first block is just a block which shows where the Int and String are extracted. Then there are three examples of a possible implementation:
> 
> Current implementation (with overhead and no possible constraints at all)  
> Ideal solution using enums, but with a feature we probably never get  
> A solution using protocols to be able to switch over the Self type in a constrained but safe manner

Maybe it's just me, but I need more English text than you have written.

You've shown "three examples of a possible implementation" ...of what? In the earlier post, you call them the "current solution" and "ideal solution" ...to what? What does your code do? Why are you extracting `Int` and `String`? Why is this an extension on `Document`?

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:02am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/30 "2018-01-27T09:02:41Z")

</div>

> [@DevAndArtist](#):
>
> Can you elaborate the source breaking change here, because I don’t see it. The staged example release does not have a source breaking change from my point of view, it just deprecates specific syntax over time, which should be allowed, or how can you justify the move from private to a fileprivate-like access modifier for Swift 4?

You cannot. The core team made it clear that changes like `private` to `fileprivate` were not to be possible after Swift 3.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:05am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/31 "2018-01-27T09:05:16Z")

</div>

> [@xwu](#):
>
> Can you give more details about what exactly you’re designing that requires an array of either Int or String? Since it’s been stated on this list that enums are the intended way of solving this issue, what ergonomics issues are you running into, and shouldn’t we be solving those?

Maybe because you asked @karim this

> [@xwu](#):
>
> Can you give more details about what exactly you’re designing that requires an array of either Int or String? Since it’s been stated on this list that enums are the intended way of solving this issue, what ergonomics issues are you running into, and shouldn’t we be solving those?

and I replied to you with an example of an array of `Int | String`! I'd very much appreciate if you'd follow the replies and don't just reply to everything in a general way ripping out the context completely. ;)

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:07am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/32 "2018-01-27T09:07:56Z")

</div>

What is it that I cannot? I didn't ask anything like that. Again you're ripping out the context and not answering to questions but expect yours to be answered. I'm sorry, but again I'd very much appreciated if you could provide constructive answers to this topic, otherwise it's very tiring to answer general questions of yours over and over again.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:08am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/33 "2018-01-27T09:08:26Z")

</div>

> [@DevAndArtist](#):
>
> and I replied to you with an example of an array of Int | String! I’d very much appreciate if you’d follow the replies and don’t just reply to everything in a general way ripping out the context completely. ;)

I still don't understand. What is the code that you posted an example of? That is to say, what does it do? I didn't ask for "an example of an array of Int | String"--I asked @karim what a _problem_ was that he encountered which required such an array. What is the problem that you're solving with your block of code?

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:09am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/34 "2018-01-27T09:09:27Z")

</div>

> [@DevAndArtist](#):
>
> What is it that I cannot? I didn’t ask anything like that. Again you’re ripping out the context and not answering to questions but expect yours to be answered. I’m sorry, but again I’d very much appreciated if you could provide constructive answers to this topic, otherwise it’s very tiring to answer general questions of your over and over again.

I think we are not understanding each other. You asked how `private` to `fileprivate` could be justified in Swift 4. I answered that it cannot be justified in Swift 4: no such change would now be acceptable.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:20am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/35 "2018-01-27T09:20:11Z")

</div>

That's more to the point now.

The code I posted above is solving one issue, it provides a subscript that allows the user to travers a document object using string keys and integer indexes.

For example if a document instance would look something like this:

```swift
[
   "key_1": /* array */ [1 , 2 , 3],
   "key_2": [
       "key_1": /* array */ [/* documents */ [ "key_1" : 42] , ["key_1" : [0, 1, 2, 99]] ]
   ]
]

```

Let's call the document instance `aDocument`. The API above allows you to travers the document like this:

```swift
let firstKey = "key_2"
let index = 1
aDocument[firstKey, index, "key_1", 3] // This will return a type `Value?` which stores the number `99` 

```

The issue with the implementation is that it still allows the user to conform to our protocol which is used to allow the variadic `Int | String` in first place. Furthermore the protection using an enum or simply a guarding type in the protocol requirements adds additional layer of overhead to the program, which could have been avoided if we had language support for closed protocols.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:27am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/36 "2018-01-27T09:27:41Z")

</div>

Okay I get it now, but you still could not elaborate why the pitched staged release would be source-breaking. Then you're saying the the core team just did the breaking change for Swift 4, but we cannot provide a change to the language that is not source breaking (my point of view, please proof me otherwise) even in move that will take quite a few major releases to keep source compatibility, just to get us to the point where we should have been in Swift 3 already (MHO).

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:30am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/37 "2018-01-27T09:30:22Z")

</div>

> [@DevAndArtist](#):
>
> The issue with the implementation is that it still allows the user to conform to our protocol which is used to allow the variadic Int | String in first place. Furthermore the protection using an enum or simply a guarding type in the protocol requirements adds additional layer of overhead to the program, which could have been avoided if we had language support for closed protocols.

There's a dramatic overhead cost to implementing this as an array of protocol existentials. If you could express closed protocols, then the overhead cost might be optimized away by a sufficiently intelligent compiler, but the overhead cost would then merely be the same as a frozen enum. In other words, frozen enum vs. closed protocol would be entirely an issue of cosmetics.

This is why I asked why it is that we weren't talking about how to make enums more ergonomic. Here, you'd want a sort of subtyping relationship between a value of type `T` or type `U` and an `enum E { case t(T); case u(U) }` and automatic promotion. I drafted a sketch of this a long time ago, which I may or may not have ever sent to the list.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 27, 2018, 9:33am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/38 "2018-01-27T09:33:25Z")

</div>

> [@DevAndArtist](#):
>
> Okay I get it now, but you still could not elaborate why the pitched staged release would be source-breaking.

Source breaking means that code that compiles in version X and means one thing ceases to compile in version Y or means a different thing where Y \> X. Changing the meaning of `public protocol` is source breaking no matter what. This requires meeting a high bar of showing active harm being cause by the existing way of doing things.

> [@DevAndArtist](#):
>
> Then you’re saying the the core team just did the breaking change for Swift 4, but we cannot provide a change to the language that is not source breaking (my point of view, please proof me otherwise) even in move that will take quite a few major releases to keep source compatibility, just to get us to the point where we should have been in Swift 3 already (MHO).

I don't understand this sentence. But changing the meaning of `public protocol` cannot be done now, I think there is fairly good consensus about that.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:33am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/39 "2018-01-27T09:33:56Z")

</div>

> [@xwu](#):
>
> Maybe it’s just me, but I need more English text than you have written.

About that, please respect that not everyone is eloquent as you might expect personally. I'm definitely not and have hard times to justify everyones expectations.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 27, 2018, 9:38am UTC](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832/40 "2018-01-27T09:38:33Z")

</div>

Hmm but isn't that all about source compatibility? We can use Swift 3 modules in Swift 4 even if the syntax has already changed. The Swift 3 module is compiled to Swift 4 in a way that Swift 4 project understands it, but it shouldn't be possible the other way around because then the language can simply not evolve any further.

[Previous page](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832.md?page=1)

[Next page](https://forums.swift.org/t/constrain-protocol-usage-outside-the-module/8832.md?page=3)
