Protocol with instance var that's set on construction, otherwise read-only


(Rick M) #1

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
    var uuid : { get }
}

extension
Element
{
    init(...)
    {
  self.uuid = ...
    }
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

···

--
Rick Mann
rmann@latencyzero.com


(David Sweeris) #2

If I understand things correctly, you *can* make uuid a let because you’re allowed to set them (once) during init functions.

- Dave Sweeris

···

On Aug 2, 2016, at 6:22 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
   var uuid : { get }
}

extension
Element
{
   init(...)
   {
  self.uuid = ...
   }
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

--
Rick Mann
rmann@latencyzero.com

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


(Rick M) #3

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.

···

On Aug 2, 2016, at 16:28 , David Sweeris <davesweeris@mac.com> wrote:

If I understand things correctly, you *can* make uuid a let because you’re allowed to set them (once) during init functions.

- Dave Sweeris

On Aug 2, 2016, at 6:22 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
  var uuid : { get }
}

extension
Element
{
  init(...)
  {
  self.uuid = ...
  }
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

--
Rick Mann
rmann@latencyzero.com

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

--
Rick Mann
rmann@latencyzero.com


(Jordan Rose) #4

I don’t think it makes sense to do this. A protocol cannot control how a particular property is implemented (stored or computed), and any conforming type must initialize all of its stored properties before returning from its own initializer. (You can’t write an initializer in a protocol that doesn’t delegate to another initializer because you don’t know what other stored properties the conforming type might have.)

Given that the protocol can’t control how the property gets initialized, it doesn’t make sense to allow the protocol to "set the variable, but only in the initializer”.

Jordan

···

On Aug 2, 2016, at 17:01, Rick Mann via swift-users <swift-users@swift.org> wrote:

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.

On Aug 2, 2016, at 16:28 , David Sweeris <davesweeris@mac.com> wrote:

If I understand things correctly, you *can* make uuid a let because you’re allowed to set them (once) during init functions.

- Dave Sweeris

On Aug 2, 2016, at 6:22 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
var uuid : { get }
}

extension
Element
{
init(...)
{
  self.uuid = ...
}
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

--
Rick Mann
rmann@latencyzero.com

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

--
Rick Mann
rmann@latencyzero.com <mailto:rmann@latencyzero.com>

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(David Sweeris) #5

Oh, it's a computed property! Got it, I thought you meant its value was computed in the init and never changed again.

···

Sent from my iPhone

On Aug 2, 2016, at 19:01, Rick Mann <rmann@latencyzero.com> wrote:

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.

On Aug 2, 2016, at 16:28 , David Sweeris <davesweeris@mac.com> wrote:

If I understand things correctly, you *can* make uuid a let because you’re allowed to set them (once) during init functions.

- Dave Sweeris

On Aug 2, 2016, at 6:22 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
var uuid : { get }
}

extension
Element
{
init(...)
{
   self.uuid = ...
}
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

--
Rick Mann
rmann@latencyzero.com

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

--
Rick Mann
rmann@latencyzero.com


(Karl) #6

It sounds like you should put the calculation function in the protocol extension, the property in the protocol, and let conforming types decide how to store that property (and when to calculate it).

···

On 3 Aug 2016, at 02:01, Rick Mann via swift-users <swift-users@swift.org> wrote:

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.


(Rick M) #7

I don’t think it makes sense to do this. A protocol cannot control how a particular property is implemented (stored or computed), and any conforming type must initialize all of its stored properties before returning from its own initializer. (You can’t write an initializer in a protocol that doesn’t delegate to another initializer because you don’t know what other stored properties the conforming type might have.)

Given that the protocol can’t control how the property gets initialized, it doesn’t make sense to allow the protocol to "set the variable, but only in the initializer”.

Really? It seems pretty natural for a conforming type to set a property once in the initializer, and it's immutable from then on out. I can do that quite cleanly with classes, but there's no way (that I know) to describe this using protocols. Ideally, I could just do:

protocol
Element
{
    let uuid: UUID
}

which implies that all conforming types must initialize that value on creation, or provide a getter with let semantics (the latter might be too easy to break, and could be disallowed, requiring conforming types to create storage for the property and set it in init()).

···

On Aug 2, 2016, at 19:06 , Jordan Rose <jordan_rose@apple.com> wrote:

Jordan

On Aug 2, 2016, at 17:01, Rick Mann via swift-users <swift-users@swift.org> wrote:

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.

On Aug 2, 2016, at 16:28 , David Sweeris <davesweeris@mac.com> wrote:

If I understand things correctly, you *can* make uuid a let because you’re allowed to set them (once) during init functions.

- Dave Sweeris

On Aug 2, 2016, at 6:22 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to define a protocol that has a read-only, immutable member "uuid" that can be set in the init() method, but I'm having trouble. I have this:

protocol
Element
{
var uuid : { get }
}

extension
Element
{
init(...)
{
  self.uuid = ...
}
}

I can't make it let, because they're computed.

I'm realizing from other stuff that I really can't have the init(...) method in the extension, anyway. But I'd really like to be able to specify a let member in the protocol. What's the best way to have that effect?

In my semantics, an Element has a uniquely-assigned uuid. It might be generated when the object is instantiated, or it might be deserialized from disk, but once that's done, it can never change. How do I express that?

Thanks,

--
Rick Mann
rmann@latencyzero.com

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

--
Rick Mann
rmann@latencyzero.com

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

--
Rick Mann
rmann@latencyzero.com


(Rick M) #8

That still doesn't mean that someone on the outside looking in at Element sees let semantics.

···

On Aug 9, 2016, at 08:24 , Karl <razielim@gmail.com> wrote:

On 3 Aug 2016, at 02:01, Rick Mann via swift-users <swift-users@swift.org> wrote:

It complains if I make it a let because computed properties must be var. Because it's a protocol, it can't be stored (even though it can be stored in the conforming type).

If I make it { get }, I can't set it in the extensions init() method.

I guess I could make it private set (not sure of the syntax for that), but it still doesn't have let semantics.

It sounds like you should put the calculation function in the protocol extension, the property in the protocol, and let conforming types decide how to store that property (and when to calculate it).

--
Rick Mann
rmann@latencyzero.com


(Dan Loewenherz) #9

The compiler only knows as much as you tell it, and when you define a
protocol and set it as { get } only, it's not going to let you set that
property in a protocol extension for a protocol that doesn't expect a
setter. Why or how would the compiler let you?

The solution is to create your init implementation in a concrete type, and
define the let property in there as well. E.g.

protocol Element {
    var uuid: UUID { get }
}

class Item: Element {
    let uuid: UUID

    init(uuid: UUID) {
        self.uuid = uuid
    }
}

···

On Wed, Aug 3, 2016 at 3:51 AM, Rick Mann via swift-users < swift-users@swift.org> wrote:

> On Aug 2, 2016, at 19:06 , Jordan Rose <jordan_rose@apple.com> wrote:
>
> I don’t think it makes sense to do this. A protocol cannot control how a
particular property is implemented (stored or computed), and any conforming
type must initialize all of its stored properties before returning from its
own initializer. (You can’t write an initializer in a protocol that doesn’t
delegate to another initializer because you don’t know what other stored
properties the conforming type might have.)
>
> Given that the protocol can’t control how the property gets initialized,
it doesn’t make sense to allow the protocol to "set the variable, but only
in the initializer”.

Really? It seems pretty natural for a conforming type to set a property
once in the initializer, and it's immutable from then on out. I can do that
quite cleanly with classes, but there's no way (that I know) to describe
this using protocols. Ideally, I could just do:

protocol
Element
{
    let uuid: UUID
}

which implies that all conforming types must initialize that value on
creation, or provide a getter with let semantics (the latter might be too
easy to break, and could be disallowed, requiring conforming types to
create storage for the property and set it in init()).


(Rick M) #10

Because then Element.uuid doesn't have let semantics. It can't be set, but it's not constant.

···

On Aug 3, 2016, at 03:23 , Dan Loewenherz <dan@lionheartsw.com> wrote:

On Wed, Aug 3, 2016 at 3:51 AM, Rick Mann via swift-users <swift-users@swift.org> wrote:

> On Aug 2, 2016, at 19:06 , Jordan Rose <jordan_rose@apple.com> wrote:
>
> I don’t think it makes sense to do this. A protocol cannot control how a particular property is implemented (stored or computed), and any conforming type must initialize all of its stored properties before returning from its own initializer. (You can’t write an initializer in a protocol that doesn’t delegate to another initializer because you don’t know what other stored properties the conforming type might have.)
>
> Given that the protocol can’t control how the property gets initialized, it doesn’t make sense to allow the protocol to "set the variable, but only in the initializer”.

Really? It seems pretty natural for a conforming type to set a property once in the initializer, and it's immutable from then on out. I can do that quite cleanly with classes, but there's no way (that I know) to describe this using protocols. Ideally, I could just do:

protocol
Element
{
    let uuid: UUID
}

which implies that all conforming types must initialize that value on creation, or provide a getter with let semantics (the latter might be too easy to break, and could be disallowed, requiring conforming types to create storage for the property and set it in init()).

The compiler only knows as much as you tell it, and when you define a protocol and set it as { get } only, it's not going to let you set that property in a protocol extension for a protocol that doesn't expect a setter. Why or how would the compiler let you?

The solution is to create your init implementation in a concrete type, and define the let property in there as well. E.g.

protocol Element {
    var uuid: UUID { get }
}

class Item: Element {
    let uuid: UUID

    init(uuid: UUID) {
        self.uuid = uuid
    }
}

--
Rick Mann
rmann@latencyzero.com


(Dan Loewenherz) #11

It's unavoidable. In this case, whether a variable can be set only once or
more than once is an implementation detail that you need to deal with in a
concrete type. What you're looking for would be a new feature, like
"setonce". But let's assume the language designers added that in, how would
it work?

protocol Element {
    var uuid: UUID { get setonce }
}

And somewhere in your codebase, you have something like this:

for element in elements {
    element.uuid = UUID()
}

The problem is that whether or not "uuid" is set can only be decided at
runtime. As a result, the type checker can't tell you if you're making a
mistake. It has no way to know if you've already set the value of "uuid"
somewhere else, e.g., through a UI interaction, or some other code that's
based on state.

The result is that if "uuid" happens to already be set, you'd get a runtime
error, which would be a very un-Swift-y thing to do, or every assignment
would need to get wrapped in some sort of error handler.

Dan

···

On Mon, Aug 8, 2016 at 6:16 PM, Rick Mann <rmann@latencyzero.com> wrote:

> On Aug 3, 2016, at 03:23 , Dan Loewenherz <dan@lionheartsw.com> wrote:
>
> On Wed, Aug 3, 2016 at 3:51 AM, Rick Mann via swift-users < > swift-users@swift.org> wrote:
>
> > On Aug 2, 2016, at 19:06 , Jordan Rose <jordan_rose@apple.com> wrote:
> >
> > I don’t think it makes sense to do this. A protocol cannot control how
a particular property is implemented (stored or computed), and any
conforming type must initialize all of its stored properties before
returning from its own initializer. (You can’t write an initializer in a
protocol that doesn’t delegate to another initializer because you don’t
know what other stored properties the conforming type might have.)
> >
> > Given that the protocol can’t control how the property gets
initialized, it doesn’t make sense to allow the protocol to "set the
variable, but only in the initializer”.
>
> Really? It seems pretty natural for a conforming type to set a property
once in the initializer, and it's immutable from then on out. I can do that
quite cleanly with classes, but there's no way (that I know) to describe
this using protocols. Ideally, I could just do:
>
> protocol
> Element
> {
> let uuid: UUID
> }
>
> which implies that all conforming types must initialize that value on
creation, or provide a getter with let semantics (the latter might be too
easy to break, and could be disallowed, requiring conforming types to
create storage for the property and set it in init()).
>
> The compiler only knows as much as you tell it, and when you define a
protocol and set it as { get } only, it's not going to let you set that
property in a protocol extension for a protocol that doesn't expect a
setter. Why or how would the compiler let you?
>
> The solution is to create your init implementation in a concrete type,
and define the let property in there as well. E.g.
>
> protocol Element {
> var uuid: UUID { get }
> }
>
> class Item: Element {
> let uuid: UUID
>
> init(uuid: UUID) {
> self.uuid = uuid
> }
> }
>

Because then Element.uuid doesn't have let semantics. It can't be set, but
it's not constant.