[Pitch] Non-class type requirements on protocols (eg : struct, : enum)


(Mike Kasianowicz) #1

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the
future. For example, a protocol describing an enum with a common case, or
a struct with no reference members. Great stuff for defensively coding
APIs.]


(Xiaodi Wu) #2

What's your use case for distinguishing structs and enums?

···

On Fri, Oct 21, 2016 at 1:40 AM Mike Kasianowicz via swift-evolution < swift-evolution@swift.org> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the
future. For example, a protocol describing an enum with a common case, or
a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #3

IIRC, the reason we have "class" there is for the optimiser, so it can optimise for the protocol being satisfied by a reference-counted type. Classes are semantically unique from values because they have identity, which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be values, but I struggle to think of why you might want to codify that a conformer should not have identity.

Personally I don't really like this asymmetry in the language either, and would support changes to make these two elements more explicit. For example, a magic "hasIdentity" protocol which is automatically satisfied only by classes, and moving the optimisation guides to usage site (e.g. when declaring a variable of type MyProto, I could declare it of type AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific instance as being refcountable or not, without making such optimisation hints part of the MyProto definition)

- Karl

···

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution (mailto:swift-evolution@swift.org)> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:

protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the future. For example, a protocol describing an enum with a common case, or a struct with no reference members. Great stuff for defensively coding APIs.]

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


(Tino) #4

As this discussion pops up again:
Am I the only one who dislikes the missing capitalization?
I don't see any value in recycling a keyword (and breaking with convention).

Imho it should be
protocol StructOnlyProtocol: Struct {
and
protocol ClassOnlyProtocol: Class {

- Tino


(Mike Kasianowicz) #5

Just from an outside perspective, the class restriction seems to be there
as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it
would be to enforce copy-by-value semantics. I think the strongest
argument I've got is, say, a serialization or caching framework where you
want to enforce that something is entirely writeable via memory pointer or
copyable. A value-type restriction would get us mostly there, albeit there
would still be ways to break the contract. However, as noted in my
previous email, I see a lot of possibilities for enums too - in that case
the protocol somewhat acts as 'base type' without adding the complexity of
a base type.

I listed some of my examples in my previous email - I could elaborate if it
helps.

···

On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com> wrote:

IIRC, the reason we have "class" there is for the optimiser, so it can
optimise for the protocol being satisfied by a reference-counted type.
Classes are semantically unique from values because they have identity,
which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be
values, but I struggle to think of why you might want to codify that a
conformer should not have identity.

Personally I don't really like this asymmetry in the language either, and
would support changes to make these two elements more explicit. For
example, a magic "hasIdentity" protocol which is automatically satisfied
only by classes, and moving the optimisation guides to usage site (e.g.
when declaring a variable of type MyProto, I could declare it of type
AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific
instance as being refcountable or not, without making such optimisation
hints part of the MyProto definition)

- Karl

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution > <swift-evolution@swift.org>> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the
future. For example, a protocol describing an enum with a common case, or
a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________ swift-evolution mailing
list swift-evolution@swift.org https://lists.swift.org/
mailman/listinfo/swift-evolution


(Adrian Zubarev) #6

We had similar talks about that topic before. There is a thread somewhere on this, but it’s mainly focused on value type semantics.

If I remember correctly we might even discussed to remove the class constraint completely from the language and introduce a protocol solution to this problem.

There are also some words about the class constraint in the GenericsManifesto.md

Generalized class constraints

The class constraint can currently only be used for defining protocols. We could generalize it to associated type and type parameter declarations, e.g.,

protocol P {
associatedtype A : class
}

func foo<T : class>(t: T) { }
As part of this, the magical AnyObject protocol could be replaced with an existential with a class bound, so that it becomes a typealias:

typealias AnyObject = protocol<class>
See the “Existentials” section, particularly “Generalized existentials”, for more information.
Instead of : class we might use : AnyObject as a constraint (or : AnyReference?).

Instead of : struct/enum we should have something like : value or even better : AnyValue.

I believe we should focus on existential types first before fixing this gap in Swift. Any<T> might receive the mentioned constraints and could create something like this:

typealias AnyObject = Any<class>
typealias AnyValue = Any<value>

···

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 08:55:40, Xiaodi Wu via swift-evolution (swift-evolution@swift.org) schrieb:

What's your use case for distinguishing structs and enums?
On Fri, Oct 21, 2016 at 1:40 AM Mike Kasianowicz via swift-evolution <swift-evolution@swift.org> wrote:
Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the future. For example, a protocol describing an enum with a common case, or a struct with no reference members. Great stuff for defensively coding APIs.]
_______________________________________________
swift-evolution mailing list
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


(Dave Abrahams) #7

I'm not sure what you mean by “data-only,” but the constraint I want is
“has value semantics.”

···

on Fri Oct 21 2016, Mike Kasianowicz <swift-evolution@swift.org> wrote:

Hooray, I'm not the only one.

I agree copying is a much more nuanced issue- but sometimes struct is close
enough.

What I would really like is "this is a data-only type" rather than
"struct".

--
-Dave


(Mike Kasianowicz) #8

Hooray, I'm not the only one.

I agree copying is a much more nuanced issue- but sometimes struct is close
enough.

What I would really like is "this is a data-only type" rather than
"struct". But short-term, I'd take the struct restriction if it's a simple
change. If we ever had a more specific constraint like that, my
hypothetical API will break many more clients if people have implemented my
protocol as a class. I'm considering runtime enforcement, but this is
clearly something that the compiler can do, and the question is whether it
should. I would argue yes - one of the great things about Swift is getting
your head out of the technical weeds and thinking at a higher level.

Protocol-oriented programming could be much more concise and allow for some
really awesome high-level things to be done and enforced - ex: MVC, MVVM,
MVP, VIPER whatever your UI approach is today - you could codify the
infrastructural paradigm and then require data-only types for the models,
or enum-only types for the events, to prevent dependency violations. Great
for making frameworks safer, enforcing coding standards, etc.

Maybe long-term there could be user-defined constraints similar to the
proposed property decorator/annotation notation?

···

On Fri, Oct 21, 2016 at 11:38 AM, T.J. Usiyan <griotspeak@gmail.com> wrote:

I would like the ability to specify that something is an enum so that I
could model a generic `Result` type.

protocol Result : enum {
    associatedtype Payload
    case success(Payload)
    case failure(Error)
}

the basic idea being that I could then, while conforming, state which
cases in the concrete type serve as the protocol's case. I don't have a
great vision for the syntax of spelling this conformance so I will make
this painfully verbose to be clear

enum UserParseResult {
    case success(User)
    case failure(Error)
}

extension UserParseResult : Result {
    protocol(Result) case success = UserParseResult.success
    protocol(Result) case failure = UserParseResult.failure
}

The benefit of this, in my opinion, is that we could have code commonly
used on results everywhere written once on the protocol without sacrificing
the ability to switch with guarantees. I can see that this suggestion has
some rough points so all I will finish by restating the problem that I want
to solve.

There is code that is fairly common to enum types that have shared
characteristics and/or purpose. I would find it useful to have a way to
implement shared algorithms in a generic way while retaining core features
of enums.

On Fri, Oct 21, 2016 at 11:11 AM, Mike Kasianowicz via swift-evolution < > swift-evolution@swift.org> wrote:

Just from an outside perspective, the class restriction seems to be there
as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it
would be to enforce copy-by-value semantics. I think the strongest
argument I've got is, say, a serialization or caching framework where you
want to enforce that something is entirely writeable via memory pointer or
copyable. A value-type restriction would get us mostly there, albeit there
would still be ways to break the contract. However, as noted in my
previous email, I see a lot of possibilities for enums too - in that case
the protocol somewhat acts as 'base type' without adding the complexity of
a base type.

I listed some of my examples in my previous email - I could elaborate if
it helps.

On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com> wrote:

IIRC, the reason we have "class" there is for the optimiser, so it can
optimise for the protocol being satisfied by a reference-counted type.
Classes are semantically unique from values because they have identity,
which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be
values, but I struggle to think of why you might want to codify that a
conformer should not have identity.

Personally I don't really like this asymmetry in the language either,
and would support changes to make these two elements more explicit. For
example, a magic "hasIdentity" protocol which is automatically satisfied
only by classes, and moving the optimisation guides to usage site (e.g.
when declaring a variable of type MyProto, I could declare it of type
AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific
instance as being refcountable or not, without making such optimisation
hints part of the MyProto definition)

- Karl

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution >>> <swift-evolution@swift.org>> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in
the future. For example, a protocol describing an enum with a common case,
or a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________ swift-evolution mailing
list swift-evolution@swift.org https://lists.swift.org/mailma
n/listinfo/swift-evolution

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


(TJ Usiyan) #9

I would like the ability to specify that something is an enum so that I
could model a generic `Result` type.

protocol Result : enum {
    associatedtype Payload
    case success(Payload)
    case failure(Error)
}

the basic idea being that I could then, while conforming, state which cases
in the concrete type serve as the protocol's case. I don't have a great
vision for the syntax of spelling this conformance so I will make this
painfully verbose to be clear

enum UserParseResult {
    case success(User)
    case failure(Error)
}

extension UserParseResult : Result {
    protocol(Result) case success = UserParseResult.success
    protocol(Result) case failure = UserParseResult.failure
}

The benefit of this, in my opinion, is that we could have code commonly
used on results everywhere written once on the protocol without sacrificing
the ability to switch with guarantees. I can see that this suggestion has
some rough points so all I will finish by restating the problem that I want
to solve.

There is code that is fairly common to enum types that have shared
characteristics and/or purpose. I would find it useful to have a way to
implement shared algorithms in a generic way while retaining core features
of enums.

···

On Fri, Oct 21, 2016 at 11:11 AM, Mike Kasianowicz via swift-evolution < swift-evolution@swift.org> wrote:

Just from an outside perspective, the class restriction seems to be there
as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it
would be to enforce copy-by-value semantics. I think the strongest
argument I've got is, say, a serialization or caching framework where you
want to enforce that something is entirely writeable via memory pointer or
copyable. A value-type restriction would get us mostly there, albeit there
would still be ways to break the contract. However, as noted in my
previous email, I see a lot of possibilities for enums too - in that case
the protocol somewhat acts as 'base type' without adding the complexity of
a base type.

I listed some of my examples in my previous email - I could elaborate if
it helps.

On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com> wrote:

IIRC, the reason we have "class" there is for the optimiser, so it can
optimise for the protocol being satisfied by a reference-counted type.
Classes are semantically unique from values because they have identity,
which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be
values, but I struggle to think of why you might want to codify that a
conformer should not have identity.

Personally I don't really like this asymmetry in the language either, and
would support changes to make these two elements more explicit. For
example, a magic "hasIdentity" protocol which is automatically satisfied
only by classes, and moving the optimisation guides to usage site (e.g.
when declaring a variable of type MyProto, I could declare it of type
AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific
instance as being refcountable or not, without making such optimisation
hints part of the MyProto definition)

- Karl

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution >> <swift-evolution@swift.org>> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in
the future. For example, a protocol describing an enum with a common case,
or a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________ swift-evolution mailing
list swift-evolution@swift.org https://lists.swift.org/mailma
n/listinfo/swift-evolution

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


(Mike Kasianowicz) #10

I mean essentially what is known as a POD type (plain ol' data) in C++. A
data payload without clever features.

In this instance, I don't *necessarily* care about whether it's a struct or
class from a 10,000 foot level, so long as it can copy and is composed of
only other value types. A struct constraint gets us half-way there, but
only incidentally by virtue of value semantics.

In the Cocoa APIs, these are often defined as an NSDictionary with
key-value pairs. You could subclass NSDictionary and mess with the
consuming API... but it's pretty clear that the consuming API only wants
data and doesn't want you to get clever about it.

(Not to detract from the enum discussion, because I really like where
that's headed re:Tony's email.)

···

On Fri, Oct 21, 2016 at 12:37 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Fri Oct 21 2016, Mike Kasianowicz <swift-evolution@swift.org> wrote:

> Hooray, I'm not the only one.
>
> I agree copying is a much more nuanced issue- but sometimes struct is
close
> enough.
>
> What I would really like is "this is a data-only type" rather than
> "struct".

I'm not sure what you mean by “data-only,” but the constraint I want is
“has value semantics.”

--
-Dave

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


(Tony Allevato) #11

I'd like to spin this a slightly different way.

While they're not exactly the same as structs because the compiler has
separate logic for exhaustiveness checking, enum cases have some
interesting properties that make them relatable:

* A case without a payload is like a static read-only property on the enum
type that is equal to a value of that type.
* A case with a payload is like a static function on the enum type that
creates and returns a value of that type.

The second point is interesting because if you have:

enum Foo {
  case bar(Int)
}

and you reference "Foo.bar" without a payload, what you get back is a
function of type (Int) -> Foo.

So instead of casting your example as "a protocol can be enum-constrained
and must contain certain cases", what if enums could just have their
conformance fall out naturally from the bullet point observations above?

In other words, your example protocol could be:

protocol Result {
  associatedtype Payload
  static func success(_ p: Payload) -> Self
  static func failure(_ e: Error) -> Self
}

and the cases provide the conformance without having to do anything extra:

enum UserParseResult: Result {
  typealias Payload = User  // can be omitted if it's inferred
  case success(User)
  case failure(Error)
}

Now there's a caveat here worth discussing: would the intention of an
"enum-constrained protocol" be to define the *only* cases it can have, or
just the minimal set? The former would let you do some interesting generic
protocol-constrained exhaustive pattern matching. However, given that no
other use of protocols defines an *exact* set (you don't say these are the
*only* methods/properties that a conforming type can implement), I think it
would be a hard sell to apply a stronger restriction specifically to enums.
In any case, it feels to me like an enum restricted to "only these exact
cases" calls more for a generic enum rather than a protocol-based solution.

···

On Fri, Oct 21, 2016 at 9:39 AM T.J. Usiyan via swift-evolution < swift-evolution@swift.org> wrote:

I would like the ability to specify that something is an enum so that I
could model a generic `Result` type.

protocol Result : enum {
    associatedtype Payload
    case success(Payload)
    case failure(Error)
}

the basic idea being that I could then, while conforming, state which
cases in the concrete type serve as the protocol's case. I don't have a
great vision for the syntax of spelling this conformance so I will make
this painfully verbose to be clear

enum UserParseResult {
    case success(User)
    case failure(Error)
}

extension UserParseResult : Result {
    protocol(Result) case success = UserParseResult.success
    protocol(Result) case failure = UserParseResult.failure
}

The benefit of this, in my opinion, is that we could have code commonly
used on results everywhere written once on the protocol without sacrificing
the ability to switch with guarantees. I can see that this suggestion has
some rough points so all I will finish by restating the problem that I want
to solve.

There is code that is fairly common to enum types that have shared
characteristics and/or purpose. I would find it useful to have a way to
implement shared algorithms in a generic way while retaining core features
of enums.

On Fri, Oct 21, 2016 at 11:11 AM, Mike Kasianowicz via swift-evolution < > swift-evolution@swift.org> wrote:

Just from an outside perspective, the class restriction seems to be there
as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it
would be to enforce copy-by-value semantics. I think the strongest
argument I've got is, say, a serialization or caching framework where you
want to enforce that something is entirely writeable via memory pointer or
copyable. A value-type restriction would get us mostly there, albeit there
would still be ways to break the contract. However, as noted in my
previous email, I see a lot of possibilities for enums too - in that case
the protocol somewhat acts as 'base type' without adding the complexity of
a base type.

I listed some of my examples in my previous email - I could elaborate if
it helps.

On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com> wrote:

IIRC, the reason we have "class" there is for the optimiser, so it can
optimise for the protocol being satisfied by a reference-counted type.
Classes are semantically unique from values because they have identity,
which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be
values, but I struggle to think of why you might want to codify that a
conformer should not have identity.

Personally I don't really like this asymmetry in the language either, and
would support changes to make these two elements more explicit. For
example, a magic "hasIdentity" protocol which is automatically satisfied
only by classes, and moving the optimisation guides to usage site (e.g.
when declaring a variable of type MyProto, I could declare it of type
AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific
instance as being refcountable or not, without making such optimisation
hints part of the MyProto definition)

- Karl

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution > <swift-evolution@swift.org>> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the
future. For example, a protocol describing an enum with a common case, or
a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________ swift-evolution mailing
list 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

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


(Adrian Zubarev) #12

This is the thread that I mentioned before: Should we rename “class” when referring to protocol conformance?

···

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 17:12:37, Mike Kasianowicz via swift-evolution (swift-evolution@swift.org) schrieb:

Just from an outside perspective, the class restriction seems to be there as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it would be to enforce copy-by-value semantics. I think the strongest argument I've got is, say, a serialization or caching framework where you want to enforce that something is entirely writeable via memory pointer or copyable. A value-type restriction would get us mostly there, albeit there would still be ways to break the contract. However, as noted in my previous email, I see a lot of possibilities for enums too - in that case the protocol somewhat acts as 'base type' without adding the complexity of a base type.

I listed some of my examples in my previous email - I could elaborate if it helps.

On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com> wrote:
IIRC, the reason we have "class" there is for the optimiser, so it can optimise for the protocol being satisfied by a reference-counted type. Classes are semantically unique from values because they have identity, which is also something a protocol might want to codify.

There may be some optimisation gains by requiring all conformers to be values, but I struggle to think of why you might want to codify that a conformer should not have identity.

Personally I don't really like this asymmetry in the language either, and would support changes to make these two elements more explicit. For example, a magic "hasIdentity" protocol which is automatically satisfied only by classes, and moving the optimisation guides to usage site (e.g. when declaring a variable of type MyProto, I could declare it of type AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific instance as being refcountable or not, without making such optimisation hints part of the MyProto definition)

- Karl

On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in the future. For example, a protocol describing an enum with a common case, or a struct with no reference members. Great stuff for defensively coding APIs.]
_______________________________________________ swift-evolution mailing list 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


(Adrian Zubarev) #13

Does this constraint exclude COW or are we still allowed to have a reference type for storage behind the scenes?

···

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 19:50:31, Dave Abrahams via swift-evolution (swift-evolution@swift.org) schrieb:

I'm not sure what you mean by “data-only,” but the constraint I want is
“has value semantics.”


(Mike Kasianowicz) #14

Apologies- I did try finding existing documentation or discussion, but all
I found was a stack overflow question asking how to do this.

The way I see it, it would be as simple as implicitly declaring protocols
on a type when a type is declared: class implies ReferenceType, struct
implies ValueType, etc.

In my opinion, this is very similar to the open/sealed argument about class
inheritance. The API should be able to more strictly communicate the
protocol usage, so it can provide more built-in functionality and better
establish its contract with the consuming code.

I can see some interesting scenarios like-

// API can return success, but relies on dependency injection to
communicate other results
protocol Result : EnumType {
    case success
}

···

---
// serialization/communication frameworks
protocol Payload : ValueType, OnlyDataType {
}

class MyPayload : Payload { } // error

---

// this type is a collection of types and not a concrete type. it better
indicates intent.
protocol ModuleDefinition : ProtocolType {
    associatedtype View
    associatedtype Model
    // etc
}

On Fri, Oct 21, 2016 at 3:25 AM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

We had similar talks about that topic before. There is a thread somewhere
on this, but it’s mainly focused on value type semantics.

If I remember correctly we might even discussed to remove the class
constraint completely from the language and introduce a protocol solution
to this problem.

There are also some words about the class constraint in the
GenericsManifesto.md
<https://github.com/apple/swift/blob/611fc78d28a5da97dd1bea40761b913b1077aef5/docs/GenericsManifesto.md#generalized-class-constraints>

Generalized class constraints

The class constraint can currently only be used for defining protocols.
We could generalize it to associated type and type parameter declarations,
e.g.,

protocol P {
associatedtype A : class
}

func foo<T : class>(t: T) { }

As part of this, the magical AnyObject protocol could be replaced with an
existential with a class bound, so that it becomes a typealias:

typealias AnyObject = protocol<class>

See the “Existentials” section, particularly “Generalized existentials”,
for more information.

   -

   Instead of : class we might use : AnyObject as a constraint (or :
   AnyReference?).
   -

   Instead of : struct/enum we should have something like : value or even
   better : AnyValue.

------------------------------

I believe we should focus on existential types first before fixing this
gap in Swift. Any<T> might receive the mentioned constraints and could
create something like this:

typealias AnyObject = Any<class>
typealias AnyValue = Any<value>

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 08:55:40, Xiaodi Wu via swift-evolution (
swift-evolution@swift.org) schrieb:

What's your use case for distinguishing structs and enums?
On Fri, Oct 21, 2016 at 1:40 AM Mike Kasianowicz via swift-evolution < > swift-evolution@swift.org> wrote:

Currently protocols can have the class constraint:
protocol MyProtocol : class {}

It would be (a) intuitive and (b) useful to allow such things as:
protocol Model : struct {} or protocol Event : enum {}

These types of restrictions can help prevent accidental anti-patterns or
misuse of APIs.

Seems simple and non-controversial... right?

[Note: I'd like to see even more heavy-handed protocol restrictions in
the future. For example, a protocol describing an enum with a common case,
or a struct with no reference members. Great stuff for defensively coding
APIs.]
_______________________________________________
swift-evolution mailing list
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

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


(Dave Abrahams) #15

Does this constraint exclude COW or are we still allowed to have a reference type for storage behind
the scenes?

It does not exclude CoW. Array has value semantics.

···

on Fri Oct 21 2016, Adrian Zubarev <adrian.zubarev-AT-devandartist.com> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 19:50:31, Dave Abrahams via swift-evolution (swift-evolution@swift.org)
schrieb:

I'm not sure what you mean by “data-only,” but the constraint I want is
“has value semantics.”

--
-Dave


(Karl) #16

Copying is a much more nuanced issue than just reference-or-value though. I would also support some good copying protocols in the standard library, but that's additive.

···

On Oct 21, 2016 at 5:11 pm, <Mike Kasianowicz (mailto:mike@ap14.com)> wrote:

Just from an outside perspective, the class restriction seems to be there as a kludge for technical reasons... but that's neither here nor there.

It is not so much to enforce a lack of identity - in the struct case, it would be to enforce copy-by-value semantics. I think the strongest argument I've got is, say, a serialization or caching framework where you want to enforce that something is entirely writeable via memory pointer or copyable. A value-type restriction would get us mostly there, albeit there would still be ways to break the contract. However, as noted in my previous email, I see a lot of possibilities for enums too - in that case the protocol somewhat acts as 'base type' without adding the complexity of a base type.

I listed some of my examples in my previous email - I could elaborate if it helps.
   
On Fri, Oct 21, 2016 at 9:51 AM, Karl Wagner <razielim@gmail.com (mailto:razielim@gmail.com)> wrote:

>
>
>
> IIRC, the reason we have "class" there is for the optimiser, so it can optimise for the protocol being satisfied by a reference-counted type. Classes are semantically unique from values because they have identity, which is also something a protocol might want to codify.
>
>
>
> There may be some optimisation gains by requiring all conformers to be values, but I struggle to think of why you might want to codify that a conformer should not have identity.
>
>
>
> Personally I don't really like this asymmetry in the language either, and would support changes to make these two elements more explicit. For example, a magic "hasIdentity" protocol which is automatically satisfied only by classes, and moving the optimisation guides to usage site (e.g. when declaring a variable of type MyProto, I could declare it of type AnyClass<MyProto> or AnyValue<MyProto> instead, to annotate this specific instance as being refcountable or not, without making such optimisation hints part of the MyProto definition)
>
>
>
>
>
>
>
>
> - Karl
>
>
>
>
>
>
> >
> >
> >
> > On Oct 21, 2016 at 8:39 am, <Mike Kasianowicz via swift-evolution (mailto:swift-evolution@swift.org)> wrote:
> >
> >
> >
> >
> >
> > Currently protocols can have the class constraint:
> > protocol MyProtocol : class {}
> >
> >
> >
> > It would be (a) intuitive and (b) useful to allow such things as:
> >
> > protocol Model : struct {} or protocol Event : enum {}
> >
> >
> >
> > These types of restrictions can help prevent accidental anti-patterns or misuse of APIs.
> >
> >
> >
> > Seems simple and non-controversial... right?
> >
> >
> >
> > [Note: I'd like to see even more heavy-handed protocol restrictions in the future. For example, a protocol describing an enum with a common case, or a struct with no reference members. Great stuff for defensively coding APIs.]
> >
> >
> >
> > _______________________________________________ swift-evolution mailing list swift-evolution@swift.org (mailto:swift-evolution@swift.org) https://lists.swift.org/mailman/listinfo/swift-evolution
> >
>
>
>
>


(David Sweeris) #17

I would say it should define the minimal set, and any switching over the generic protocol type needs either a `default` or a `case _` clause to handle concrete types which have more cases than the protocol requires.

- Dave Sweeris

···

On Oct 21, 2016, at 12:23 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

Now there's a caveat here worth discussing: would the intention of an "enum-constrained protocol" be to define the *only* cases it can have, or just the minimal set? The former would let you do some interesting generic protocol-constrained exhaustive pattern matching. However, given that no other use of protocols defines an *exact* set (you don't say these are the *only* methods/properties that a conforming type can implement), I think it would be a hard sell to apply a stronger restriction specifically to enums. In any case, it feels to me like an enum restricted to "only these exact cases" calls more for a generic enum rather than a protocol-based solution.


(Adrian Zubarev) #18

Sounds good to me :slight_smile:

···

--
Adrian Zubarev
Sent with Airmail

Am 21. Oktober 2016 um 21:11:22, Dave Abrahams (dabrahams@apple.com(mailto:dabrahams@apple.com)) schrieb:

on Fri Oct 21 2016, Adrian Zubarev <adrian.zubarev-AT-devandartist.com> wrote:

> Does this constraint exclude COW or are we still allowed to have a reference type for storage behind
> the scenes?

It does not exclude CoW. Array has value semantics.

> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 21. Oktober 2016 um 19:50:31, Dave Abrahams via swift-evolution (swift-evolution@swift.org)
> schrieb:
>
> I'm not sure what you mean by “data-only,” but the constraint I want is
> “has value semantics.”

--
-Dave


(TJ Usiyan) #19

Thanks for the heads up! It wasn't just meant for you. Use of a generic
Result type is one of my motivating cases.

1. If multiple libraries declare a generic type like that, converting
between them becomes a pain.
2. Specific result types are useful and appropriate in many, if not most,
cases.
3. protocols and retroactive conformance could make this less challenging

···

On Sat, Oct 22, 2016 at 3:42 PM, David Sweeris <davesweeris@mac.com> wrote:

Was not sending your reply only to me and not the list intentional? It’s
fine either way, I was just about to send this to the list when I realized
you’d only sent your reply to me, and I don’t want quote you somewhere if
you intentionally didn’t post there.

On Oct 22, 2016, at 1:39 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:

I would actually hope otherwise. A real benefit of enums is compile time
guarantees. I am looking for a generic means to represent and handle a set
of possible outcomes. Not in the hopes of making everything fully generic
but lessening the redundant parts of dealing with, for example, Result
types.

I think you can do that now...
enum Result<Payload> {
    case success(Payload)
    case failure(Error)
}

Then in your function…
func foo<T> (r: Result<T>) {
    ...
    switch r {
    case .success(let p): ...
    case .failure(let e): ...
    }
    ...
}

Or am I just not understanding you?

- Dave Sweeris


(Mike Pearce) #20

https://docs.docker.com/toolbox/toolbox_install_mac/

···

On 23 October 2016 at 06:56, T.J. Usiyan via swift-evolution < swift-evolution@swift.org> wrote:

Thanks for the heads up! It wasn't just meant for you. Use of a generic
Result type is one of my motivating cases.

1. If multiple libraries declare a generic type like that, converting
between them becomes a pain.
2. Specific result types are useful and appropriate in many, if not most,
cases.
3. protocols and retroactive conformance could make this less challenging

On Sat, Oct 22, 2016 at 3:42 PM, David Sweeris <davesweeris@mac.com> > wrote:

Was not sending your reply only to me and not the list intentional? It’s
fine either way, I was just about to send this to the list when I realized
you’d only sent your reply to me, and I don’t want quote you somewhere if
you intentionally didn’t post there.

On Oct 22, 2016, at 1:39 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:

I would actually hope otherwise. A real benefit of enums is compile time
guarantees. I am looking for a generic means to represent and handle a set
of possible outcomes. Not in the hopes of making everything fully generic
but lessening the redundant parts of dealing with, for example, Result
types.

I think you can do that now...
enum Result<Payload> {
    case success(Payload)
    case failure(Error)
}

Then in your function…
func foo<T> (r: Result<T>) {
    ...
    switch r {
    case .success(let p): ...
    case .failure(let e): ...
    }
    ...
}

Or am I just not understanding you?

- Dave Sweeris

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

--