Remove Failable Initializers

There are established guidelines for Swift error handling. The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

The duality is that an error reported through the throwing mechanism should be non-trivial. For that reason, I am wary of using the try? keyword in general (as it discards potentially meaningful information) and I'm happy that initializers are allowed to be failable.

Félix

···

Le 3 mars 2016 à 15:40:07, Haravikk via swift-evolution swift-evolution@swift.org a écrit :

On 3 Mar 2016, at 09:20, Brent Royal-Gordon brent@architechies.com wrote:
it's really nice to be able to use if-let, guard-let, or ?? to handle them instead of having to write an entire do/catch block.

What’s wrong with:

guard let myInstance = try? ErrorThrowingType() else { return }

This has the exact same flexibility, and works just as well with the ?? operator as a failable initialiser, in fact that’s one of the main reasons why try? was added I think.

Personally I think what it boils down to for me is that these initialisers are failing, which means that an error has occurred, which means that they should use error handling. On the other side I don’t see optionals as representing errors (and they shouldn’t be used to anymore IMO), but instead the result of something that doesn’t have to return a value (but has been used correctly).


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

There are established guidelines for Swift error handling.

That’s fine, but this proposal would change that so I’m sure it’s really relevant. No one’s arguing that the current advice is bad, as it’s fine given that failable initialisers currently exist, but if they were to be removed then that advice simply needs to change.

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

That means we really have two identical features, one of which has greater flexibility, so does it make sense to keep the less capable variant?

The duality is that an error reported through the throwing mechanism should be non-trivial.

Is calling an initialiser with invalid values really trivial?

I am wary of using the try? keyword in general (as it discards potentially meaningful information) and I'm happy that initializers are allowed to be failable.

The difference between try? discarding potentially useful information and a failable initialiser is that the failable initialiser provides no potentially useful information. That to me is a reason against failable initialisers, as they’re simply providing a generic failure condition in the form of nil. Besides, I don’t think that try? has inherent problems, it explicitly indicates that you are aware that the initialiser can fail, and choose to ignore the actual error in favour of working with nil instead (or providing a default with ??). Sometimes the reason for something failing just doesn’t matter to you, only that it did fail.

That’s not to say that throwable initialisers can’t just return a single error type either; they absolutely can. The benefit of them over failable initialisers is that they could produce any number of useful errors depending upon what can actually go wrong.

···

On 3 Mar 2016, at 20:53, Félix Cloutier <felixcca@yahoo.ca> wrote:

So you would you be happy if failable inits were declared like this:

init() -> self? {}

Unfortunately, Swift decided to make init() a special case thing. It's also the only function type that has special rules around accessing properties, the only one that cannot be named differently, the only one that has special rules around recursing or forwarding to other init() calls, and many other special rules and considerations.

The ability to use optionals is one of the key components to Swift's error handling story. Why should initializers not be able to participate in that story? I don't agree with your consistency argument; init() is already completely inconsistent from every other function in Swift. It's a side-effect of all of the other choices around init() that cause the inconsistent syntax with other functions signatures.

-David

···

On Mar 9, 2016, at 9:20 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

let i: Int = Int.init(123)
would not compile if that initializer had a return type of Void rather than Int.

No one can argue the observation that init-methods have return values (although I think it's rather uncommon to invoke them that way) — but that is not my point:
Let's imagine I write a library with a numeric type, and define the "+"-operator to perform a multiplication.
Given this,
let x: StringIntLiteralConvertible = 2 + 5
should assign x a value of 10: That is how the operation is defined (like "init" is defined to return self), and according to your argumentation, everything is fine.
Nonetheless, I'd say this operator is inconsistent, because it contradicts to common behavior, and "+" should be linked to addition.
Initializers behave similar (on another level, though):
They have the signature of a void function, they have the body of a void function — but yet they return something.

You are right that this is actually a topic on its own, as "init?" just emphasizes the "oddity", because suddenly there is a need to manually add a return statement for one case...

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

Given that the Proposal has been rejected I've been thinking alot of this
may be cleared up if optionals had a consistent syntax.

Object()
OptionalObject()

Are the same, as are

value
optionalValue
forcedUnwrappedValue

and:

function()
optionalFunction()
forcedUnwrappedValue()

But when it comes to protocols, I've had to write it like this:

optionalProtocolMethod()
forcedProtocolMethod!()

I would love to take a page out of Ruby's book which has ! for things that
are dangerous and ? for boolean expression. Instead for Swift we could have
? for optional returns and ! for forced unwrapped.

So we would have:

Object()
OptionalObject?()

value
optionalValue?
forcedUnwrappedValue!

function()
optionalFunction?()
forcedUnwrappedValue!()

optionalProtocolMethod?()
forcedProtocolMethod!()

You may think this is silly but I've had crashes due to function arguments
being mapped into swift from objective-c as forced un-wrapped due to the
lack of non-null and null marking.

If the name of the property had a "!" in it then I would have noticed it.

The same with falliable initializers. It would be a lot clearer to me that
Object?() could fail.

IT also confuses me that some non-optional protocol methods have to have
the ! in the name.

···

*___________________________________*

*James⎥Head of Trolls*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Wed, Mar 9, 2016 at 6:10 PM, David Owens II via swift-evolution < swift-evolution@swift.org> wrote:

So you would you be happy if failable inits were declared like this:

init() -> self? {}

Unfortunately, Swift decided to make init() a special case thing. It's
also the only function type that has special rules around accessing
properties, the only one that cannot be named differently, the only one
that has special rules around recursing or forwarding to other init()
calls, and many other special rules and considerations.

The ability to use optionals is one of the key components to Swift's error
handling story. Why should initializers not be able to participate in that
story? I don't agree with your consistency argument; init() is already
completely inconsistent from every other function in Swift. It's a
side-effect of all of the other choices around init() that cause the
inconsistent syntax with other functions signatures.

-David

On Mar 9, 2016, at 9:20 AM, Tino Heth via swift-evolution < > swift-evolution@swift.org> wrote:

let i: Int = Int.init(123)
would not compile if that initializer had a return type of Void rather
than Int.

No one can argue the observation that init-methods have return values
(although I think it's rather uncommon to invoke them that way) — but that
is not my point:
Let's imagine I write a library with a numeric type, and define the
"+"-operator to perform a multiplication.
Given this,
let x: StringIntLiteralConvertible = 2 + 5
should assign x a value of 10: That is how the operation is defined (like
"init" is defined to return self), and according to your argumentation,
everything is fine.
Nonetheless, I'd say this operator is inconsistent, because it contradicts
to common behavior, and "+" should be linked to addition.
Initializers behave similar (on another level, though):
They have the signature of a void function, they have the body of a void
function — but yet they return something.

You are right that this is actually a topic on its own, as "init?" just
emphasizes the "oddity", because suddenly there is a need to manually add a
return statement for one case...

Tino
_______________________________________________
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

So you would you be happy if failable inits were declared like this:

init() -> self? {}

Not really — afaics, the signature ought to be
static init() -> Self? {
//…
  return self
}
for maximum consistency ;-) (oh, wait: self is available in the body, so it can't be static… it's even more special than that :-)
The status quo is definitely convenient, and before this thread, I didn't brood over possible flaws it might have; now, I have to admit that initialization in Objective-C not only had the downside of being more cumbersome, but also the advantage of being simpler and more lightweight.

Unfortunately, Swift decided to make init() a special case thing. It's also the only function type that has special rules around accessing properties, the only one that cannot be named differently, the only one that has special rules around recursing or forwarding to other init() calls

True, but I think those rules can't be removed (maybe it's possible to generalize some; "required" is a candidate for this).

Apparently, getting init right is tougher than it seems at first sight (I'd love to see a brilliant answer for the "make class initialization complexity more progressively disclosed" thread that magically simplifies the whole thing ;-)

Tino

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

If there's a "general purpose invalid argument error", then that error is not communicating anything more than an optional would. It's merely taking a lot more syntax on both sides of the call, plus complexity in the calling convention and space in memory (40 bytes for a protocol witness, instead of 0-1 bytes for the overhead of an Optional!), to convey the same single bit of information: "Your data was wrong".

And if there isn't a general-purpose error, you're adding even *more* syntax and complexity—an entire new type to conform to ErrorType—and once again only conveying the same single bit of information: "Your data was wrong".

Forcing simple errors to use `throws` introduces a lot more complexity for no apparent benefit.

···

--
Brent Royal-Gordon
Architechies

There is no way to encode that an initializer can only fail because of a simple programming error when all you have next to it is "throws". When I see "init?", I know that the function can fail because I didn't check parameters. When I see "init() throws", I know that it can fail for a more complex reason, like failing to get a handle to some resource. The exception object will tell me why and if I don't have a shot at recovery, I expect to have at least something to tell the user.

I have no intention of developing a habit of writing try? in front of initializers when I can't discern the reason that they can fail.

Félix

···

Le 3 mars 2016 à 16:43:19, Haravikk <swift-evolution@haravikk.me> a écrit :

On 3 Mar 2016, at 20:53, Félix Cloutier <felixcca@yahoo.ca> wrote:

There are established guidelines for Swift error handling.

That’s fine, but this proposal would change that so I’m sure it’s really relevant. No one’s arguing that the current advice is bad, as it’s fine given that failable initialisers currently exist, but if they were to be removed then that advice simply needs to change.

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

That means we really have two identical features, one of which has greater flexibility, so does it make sense to keep the less capable variant?

The duality is that an error reported through the throwing mechanism should be non-trivial.

Is calling an initialiser with invalid values really trivial?

I am wary of using the try? keyword in general (as it discards potentially meaningful information) and I'm happy that initializers are allowed to be failable.

The difference between try? discarding potentially useful information and a failable initialiser is that the failable initialiser provides no potentially useful information. That to me is a reason against failable initialisers, as they’re simply providing a generic failure condition in the form of nil. Besides, I don’t think that try? has inherent problems, it explicitly indicates that you are aware that the initialiser can fail, and choose to ignore the actual error in favour of working with nil instead (or providing a default with ??). Sometimes the reason for something failing just doesn’t matter to you, only that it did fail.

That’s not to say that throwable initialisers can’t just return a single error type either; they absolutely can. The benefit of them over failable initialisers is that they could produce any number of useful errors depending upon what can actually go wrong.

That’s more an issue of optimisation IMO; if an initialiser (or method) is being called with try? or try! then it should be possible to optimise away the actual error generation and throwing, since it won’t be used anyway. I don’t know enough about that to know what’s currently being done in these cases, but with optimisations in place the only added complexity should be picking a relevant error to throw and then throwing it.*

Also, I’m not convinced by the idea that failable initialisers always represent only a single type of error anyway; the most common example I’ve seen is people using NSImage, but that can actually fail for several different reasons, such as a non-existent file, or the file is unreadable due to permission issues and such. While you could argue that these all stem from the parameter choice being wrong, you can argue that about a lot of things. I’d say in that case at least there should be multiple different error types for different potential types of failure, otherwise you’re left having to figure out why the issue occurred for yourself; consider for example if the file being opened were a temporary file created with the wrong permissions, it may be deleted if execution halts, leaving you only with the knowledge that something failed in NSImage(), which is why I think it’s better practice to use error handling in general as you can instead catch that error to find out what is actually wrong.

This can be true even in the supposedly simple cases; consider a simple type that is initialised from a numeric value in string format. You might say that that’s fine as an optional, but what if the string fails simply because it contains a single space character? This could be pretty non-obvious, and with most failable initialisers you’re left with nothing other than “your data was wrong” (was it malformed, did it contain invalid characters, is the value too large or too small?); this can be even worse if you include other unusual unicode characters that could look valid. If this initialiser instead returned an error type indicating that it encountered a non-numeric string, then it would leave no doubt where the error lies; granted using try? you won’t see this, but you have the option of catching the error instead to find out what it was*.

Of course adding new error types is extra work, but that’s partly an argument for the standard library to contain a good range of general purpose types, which would likely have to be added anyway while replacing failable initialisers. Even if developers fall back to a vague “InvalidParameter” error type, they could at least provide a message with it, which is still more information which is IMO always a good thing.

*Just a note, but ideally when debugging no optimisation of errors should occur, as this would give us the option of finding out what causes a try? statement to fail; this may even already be possible, I’m not sure.

···

On 3 Mar 2016, at 22:27, Brent Royal-Gordon <brent@architechies.com> wrote:

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

If there's a "general purpose invalid argument error", then that error is not communicating anything more than an optional would. It's merely taking a lot more syntax on both sides of the call, plus complexity in the calling convention and space in memory (40 bytes for a protocol witness, instead of 0-1 bytes for the overhead of an Optional!), to convey the same single bit of information: "Your data was wrong”.

I prefer `try? Int(someString)` over `Int(someString)`.

   - There's visually nothing to indicate that `Int(someString)` could fail.
   - It simplifies the language to not have failable initialisers.

InvalidArgument could be part of the standard library, something like this:

struct InvalidArgument: ErrorType {}

Brent makes a good point:

"40 bytes for a protocol witness, instead of 0-1 bytes for the overhead of
an Optional".

I wonder if it a typed throw would have similar overhead to an optional,
possibly less if the throw only has an overhead if it is thrown
<Exception Handling in LLVM — LLVM 18.0.0git documentation;
:

init (_ fromString: String) throws(InvalidArgument)

I don't know what happened to the proposal to allow throws to specify zero
or one type. I'm reluctant to use throws in general without that proposal.

···

On Fri, Mar 4, 2016 at 9:27 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

>> The guidance is that returning nil is appropriate when your function
could fail due to simple domain errors, for instance passing a negative
value as a parameter where they aren't allowed.
>
> If that’s all you need then what’s wrong with throwing some generic
purpose illegal argument type exception and using try? It’s functionally
the same, i.e- removing failable initialisers doesn’t actually remove a
capability, it just removes what is essentially now a redundant one. In
other words, try? on a throwable initialiser is now functionally identical
to a failable initialiser and isn't appreciably more complex, especially if
there’s a common exception that can be thrown for general purpose invalid
argument errors (not sure if there is one right now, but it could be added
to the proposal).

If there's a "general purpose invalid argument error", then that error is
not communicating anything more than an optional would. It's merely taking
a lot more syntax on both sides of the call, plus complexity in the calling
convention and space in memory (40 bytes for a protocol witness, instead of
0-1 bytes for the overhead of an Optional!), to convey the same single bit
of information: "Your data was wrong".

And if there isn't a general-purpose error, you're adding even *more*
syntax and complexity—an entire new type to conform to ErrorType—and once
again only conveying the same single bit of information: "Your data was
wrong".

Forcing simple errors to use `throws` introduces a lot more complexity for
no apparent benefit.

--
Brent Royal-Gordon
Architechies

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

I agree wholeheartedly.

-Thorsten

···

Am 03.03.2016 um 23:07 schrieb Félix Cloutier via swift-evolution <swift-evolution@swift.org>:

There is no way to encode that an initializer can only fail because of a simple programming error when all you have next to it is "throws". When I see "init?", I know that the function can fail because I didn't check parameters. When I see "init() throws", I know that it can fail for a more complex reason, like failing to get a handle to some resource. The exception object will tell me why and if I don't have a shot at recovery, I expect to have at least something to tell the user.

I have no intention of developing a habit of writing try? in front of initializers when I can't discern the reason that they can fail.

Félix

Le 3 mars 2016 à 16:43:19, Haravikk <swift-evolution@haravikk.me> a écrit :

On 3 Mar 2016, at 20:53, Félix Cloutier <felixcca@yahoo.ca> wrote:

There are established guidelines for Swift error handling.

That’s fine, but this proposal would change that so I’m sure it’s really relevant. No one’s arguing that the current advice is bad, as it’s fine given that failable initialisers currently exist, but if they were to be removed then that advice simply needs to change.

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

That means we really have two identical features, one of which has greater flexibility, so does it make sense to keep the less capable variant?

The duality is that an error reported through the throwing mechanism should be non-trivial.

Is calling an initialiser with invalid values really trivial?

I am wary of using the try? keyword in general (as it discards potentially meaningful information) and I'm happy that initializers are allowed to be failable.

The difference between try? discarding potentially useful information and a failable initialiser is that the failable initialiser provides no potentially useful information. That to me is a reason against failable initialisers, as they’re simply providing a generic failure condition in the form of nil. Besides, I don’t think that try? has inherent problems, it explicitly indicates that you are aware that the initialiser can fail, and choose to ignore the actual error in favour of working with nil instead (or providing a default with ??). Sometimes the reason for something failing just doesn’t matter to you, only that it did fail.

That’s not to say that throwable initialisers can’t just return a single error type either; they absolutely can. The benefit of them over failable initialisers is that they could produce any number of useful errors depending upon what can actually go wrong.

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

Without responding to anything else, there are several cases where the work to validate that a parameter is valid is pretty much equivalent to the work to create the instance. It's a pragmatic decision, then, to accept "invalid" values and return nil rather than trapping out with a precondition(_:).

That doesn't mean an error couldn't be thrown either, but such invalid parameters are usually intended to be handled right at the call site, while thrown errors are very often meant to be propagated up to the start of the activity.

Jordan

···

On Mar 3, 2016, at 13:43, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

The duality is that an error reported through the throwing mechanism should be non-trivial.

Is calling an initialiser with invalid values really trivial?

I would love to take a page out of Ruby's book which has ! for things that are dangerous and ? for boolean expression. Instead for Swift we could have ? for optional returns and ! for forced unwrapped.

So we would have:

Object()
OptionalObject?()

value
optionalValue?
forcedUnwrappedValue!

function()
optionalFunction?()
forcedUnwrappedValue!()

optionalProtocolMethod?()
forcedProtocolMethod!()

You do understand that `?` at the end of a symbol already means something else, don't you? That's how optional chaining works.

···

--
Brent Royal-Gordon
Architechies

It is more cumbersome because it's significantly more powerful and tied to a class-hierarchy. That power also makes it "unsafe". Like most recent languages, Swift has gone the route of combining both allocation and initialization into one unifying concept. This is primary source of the init() constraints, limitations, and complexity. There's a lot to get right when your stance is that a type must be fully initialized before returning it to the caller.

-David

···

On Mar 9, 2016, at 12:02 PM, Tino Heth <2th@gmx.de> wrote:

I have to admit that initialization in Objective-C not only had the downside of being more cumbersome, but also the advantage of being simpler and more lightweight.

I have to admit that initialization in Objective-C not only had the downside of being more cumbersome, but also the advantage of being simpler and more lightweight.

It is more cumbersome because it's significantly more powerful and tied to a class-hierarchy. That power also makes it "unsafe". Like most recent languages, Swift has gone the route of combining both allocation and initialization into one unifying concept. This is primary source of the init() constraints, limitations, and complexity. There's a lot to get right when your stance is that a type must be fully initialized before returning it to the caller.

This is not correct. Even if Swift had separated allocation and initialization, you would still have all of the "constraints, limitations, and complexity". It's the full initialization that forces this, not the combination with allocation.

Every rule Swift enforces was a "rule" in Objective-C; you were just free to ignore it there. Between safety and convenience, Swift chooses safety. That said, I'm/we're still hoping to come with simpler, improved initialization rules. (I should really post my summary of problems with the current model somewhere. It probably doesn't cover all the problems, but it's a good starting point for discussion.)

Additionally, from earlier:

Unfortunately, Swift decided to make init() a special case thing. It's also the only function type that has special rules around accessing properties, the only one that cannot be named differently, the only one that has special rules around recursing or forwarding to other init() calls, and many other special rules and considerations.

I disagree with most of this. Property and subscript accessors, deinitializers, and closure expressions all have different rules from plain old 'func' (which itself behaves differently as a type member vs. top-level). Objective-C makes all of these things methods, which is very uniform but causes problems where they should differ in behavior.

Jordan

···

On Mar 9, 2016, at 12:35, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 9, 2016, at 12:02 PM, Tino Heth <2th@gmx.de <mailto:2th@gmx.de>> wrote:

I have to admit that initialization in Objective-C not only had the downside of being more cumbersome, but also the advantage of being simpler and more lightweight.

It is more cumbersome because it's significantly more powerful and tied to a class-hierarchy. That power also makes it "unsafe". Like most recent languages, Swift has gone the route of combining both allocation and initialization into one unifying concept. This is primary source of the init() constraints, limitations, and complexity. There's a lot to get right when your stance is that a type must be fully initialized before returning it to the caller.

This is not correct. Even if Swift had separated allocation and initialization, you would still have all of the "constraints, limitations, and complexity". It's the full initialization that forces this, not the combination with allocation.

There are the rules for how types are instantiated and there are rules for how they are initialized, both have "constraints, limitations, and complexity". I agree that what I wrote conflated to the two issues together, and lead to a mostly ambiguous statement.

Part of what I was referring the black-box that is allocation of and lifecycle management of types that we really have no control over today. For instance, I cannot tell Swift to create my object out of a specific data region that Iv'e created. I can pre-allocate, what is essentially an array of those types, but then I cannot use init() anymore to ensure that I have nice, clean instantiations. Of course, I can't call a shared `reset` either from the init(), so I end up with duplicated code.

ObjC allowed us to implement these, maybe it wasn't safe or we won't supposed to, but it was useful. Similarly, we can do this in C or C++. Of course, these methods aren't "safe" as they don't guarantee us fully instantiated types. Like you said: trade-off between safety and convenience.

If we get custom allocators, or something similar, then this goes a long way to solving many of those types of limitations for init(). Of course, there's still the other set of limitations and complexities for ensuring that a type is fully instantiated. This also probably requires some work around owned memory allocations in order to enable the safety that Swift is looking for.

Every rule Swift enforces was a "rule" in Objective-C; you were just free to ignore it there. Between safety and convenience, Swift chooses safety. That said, I'm/we're still hoping to come with simpler, improved initialization rules. (I should really post my summary of problems with the current model somewhere. It probably doesn't cover all the problems, but it's a good starting point for discussion.)

It would be great to see.

Additionally, from earlier:

Unfortunately, Swift decided to make init() a special case thing. It's also the only function type that has special rules around accessing properties, the only one that cannot be named differently, the only one that has special rules around recursing or forwarding to other init() calls, and many other special rules and considerations.

I disagree with most of this. Property and subscript accessors, deinitializers, and closure expressions all have different rules from plain old 'func' (which itself behaves differently as a type member vs. top-level). Objective-C makes all of these things methods, which is very uniform but causes problems where they should differ in behavior.

Sure... not all people are happy about those differences =). Regardless, but those are relatively minor compared to how init() works.

-David

···

On Mar 9, 2016, at 1:15 PM, Jordan Rose <jordan_rose@apple.com> wrote:

On Mar 9, 2016, at 12:35, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mar 9, 2016, at 12:02 PM, Tino Heth <2th@gmx.de <mailto:2th@gmx.de>> wrote:

Yes I understand but I was still able to miss the fact a variable imported
from objective C as a !.

Take this example:

//Imported From Obj-C Library
typealias MyClosure = (value: NSObject!) -> Void

func loadData(block: MyClosure)

//Implemented In Swift File else where
loadData {
value in
print(value) //Crashes if we try to access it
}

This would be clearer to me:

//Imported From Obj-C Library
typealias MyClosure = (value: NSObject!) -> Void

func loadData(block: MyClosure)

//Implemented In Swift File else where
loadData {

//Since the property is a unwrapped optional, the compiler enforces us to
use the ! after the variable name
value! in
print(value!) //Still crashes if we try to access it but at least we know
its an un-wrapped type.
}

This has happened to me a few times with Librarys in Objective-C and I have
released an app that crashed because of that reason.

···

*___________________________________*

*James⎥Head of Trolls*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Wed, Mar 9, 2016 at 8:23 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

> I would love to take a page out of Ruby's book which has ! for things
that are dangerous and ? for boolean expression. Instead for Swift we could
have ? for optional returns and ! for forced unwrapped.
>
> So we would have:
>
> Object()
> OptionalObject?()
>
> value
> optionalValue?
> forcedUnwrappedValue!
>
> function()
> optionalFunction?()
> forcedUnwrappedValue!()
>
> optionalProtocolMethod?()
> forcedProtocolMethod!()

You do understand that `?` at the end of a symbol already means something
else, don't you? That's how optional chaining works.

--
Brent Royal-Gordon
Architechies

If anything, the fact that NSImage doesn't respect the error handling guidelines is an argument in favor of reworking the NSImage interface, not doing away with the guidelines. All (I think?) of the Foundation class initializers accept an inout error parameter, which translate to throws in Swift.

Consider reading Joe Duffy's writeup on error handling in Midori <Joe Duffy - The Error Model; which presents "abandonments" (what I think failable initializers model best) and actual exceptions (what I think throwing models best).

Félix

···

Le 4 mars 2016 à 04:54:52, Haravikk <swift-evolution@haravikk.me> a écrit :

On 3 Mar 2016, at 22:27, Brent Royal-Gordon <brent@architechies.com> wrote:

The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.

If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).

If there's a "general purpose invalid argument error", then that error is not communicating anything more than an optional would. It's merely taking a lot more syntax on both sides of the call, plus complexity in the calling convention and space in memory (40 bytes for a protocol witness, instead of 0-1 bytes for the overhead of an Optional!), to convey the same single bit of information: "Your data was wrong”.

That’s more an issue of optimisation IMO; if an initialiser (or method) is being called with try? or try! then it should be possible to optimise away the actual error generation and throwing, since it won’t be used anyway. I don’t know enough about that to know what’s currently being done in these cases, but with optimisations in place the only added complexity should be picking a relevant error to throw and then throwing it.*

Also, I’m not convinced by the idea that failable initialisers always represent only a single type of error anyway; the most common example I’ve seen is people using NSImage, but that can actually fail for several different reasons, such as a non-existent file, or the file is unreadable due to permission issues and such. While you could argue that these all stem from the parameter choice being wrong, you can argue that about a lot of things. I’d say in that case at least there should be multiple different error types for different potential types of failure, otherwise you’re left having to figure out why the issue occurred for yourself; consider for example if the file being opened were a temporary file created with the wrong permissions, it may be deleted if execution halts, leaving you only with the knowledge that something failed in NSImage(), which is why I think it’s better practice to use error handling in general as you can instead catch that error to find out what is actually wrong.

This can be true even in the supposedly simple cases; consider a simple type that is initialised from a numeric value in string format. You might say that that’s fine as an optional, but what if the string fails simply because it contains a single space character? This could be pretty non-obvious, and with most failable initialisers you’re left with nothing other than “your data was wrong” (was it malformed, did it contain invalid characters, is the value too large or too small?); this can be even worse if you include other unusual unicode characters that could look valid. If this initialiser instead returned an error type indicating that it encountered a non-numeric string, then it would leave no doubt where the error lies; granted using try? you won’t see this, but you have the option of catching the error instead to find out what it was*.

Of course adding new error types is extra work, but that’s partly an argument for the standard library to contain a good range of general purpose types, which would likely have to be added anyway while replacing failable initialisers. Even if developers fall back to a vague “InvalidParameter” error type, they could at least provide a message with it, which is still more information which is IMO always a good thing.

*Just a note, but ideally when debugging no optimisation of errors should occur, as this would give us the option of finding out what causes a try? statement to fail; this may even already be possible, I’m not sure.

I'm going to write up a formal proposal, at the very least perhaps the
documentation everyone is linking to could be made a formal part of the API
Guidelines on how to handle returning errors.

···

*___________________________________*

*James⎥Head of Trolls*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Fri, Mar 4, 2016 at 11:13 AM, Andrew Bennett via swift-evolution < swift-evolution@swift.org> wrote:

I prefer `try? Int(someString)` over `Int(someString)`.

   - There's visually nothing to indicate that `Int(someString)` could
   fail.
   - It simplifies the language to not have failable initialisers.

InvalidArgument could be part of the standard library, something like
this:

struct InvalidArgument: ErrorType {}

Brent makes a good point:

"40 bytes for a protocol witness, instead of 0-1 bytes for the overhead
of an Optional".

I wonder if it a typed throw would have similar overhead to an optional,
possibly less if the throw only has an overhead if it is thrown
<Exception Handling in LLVM — LLVM 18.0.0git documentation;
:

init (_ fromString: String) throws(InvalidArgument)

I don't know what happened to the proposal to allow throws to specify zero
or one type. I'm reluctant to use throws in general without that proposal.

On Fri, Mar 4, 2016 at 9:27 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

>> The guidance is that returning nil is appropriate when your function
could fail due to simple domain errors, for instance passing a negative
value as a parameter where they aren't allowed.
>
> If that’s all you need then what’s wrong with throwing some generic
purpose illegal argument type exception and using try? It’s functionally
the same, i.e- removing failable initialisers doesn’t actually remove a
capability, it just removes what is essentially now a redundant one. In
other words, try? on a throwable initialiser is now functionally identical
to a failable initialiser and isn't appreciably more complex, especially if
there’s a common exception that can be thrown for general purpose invalid
argument errors (not sure if there is one right now, but it could be added
to the proposal).

If there's a "general purpose invalid argument error", then that error is
not communicating anything more than an optional would. It's merely taking
a lot more syntax on both sides of the call, plus complexity in the calling
convention and space in memory (40 bytes for a protocol witness, instead of
0-1 bytes for the overhead of an Optional!), to convey the same single bit
of information: "Your data was wrong".

And if there isn't a general-purpose error, you're adding even *more*
syntax and complexity—an entire new type to conform to ErrorType—and once
again only conveying the same single bit of information: "Your data was
wrong".

Forcing simple errors to use `throws` introduces a lot more complexity
for no apparent benefit.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

That's not true, as the return type is an optional. This is not only visible but even has to be handled explicitly.

-Thorsten

···

Am 04.03.2016 um 12:13 schrieb Andrew Bennett via swift-evolution <swift-evolution@swift.org>:

There's visually nothing to indicate that `Int(someString)` could fail.

If anything, the fact that NSImage doesn't respect the error handling guidelines is an argument in favor of reworking the NSImage interface, not doing away with the guidelines. All (I think?) of the Foundation class initializers accept an inout error parameter, which translate to throws in Swift.

True, but even when you’re writing a type in pure Swift from the start, once you have a failable initialiser it can be easy to just return nil when you think of other possible error conditions that didn’t occur to you sooner (I did this a couple of times myself before try? was added and I switch to error handling only). While someone might do the same with an InvalidArgument error type, as long as it can take a message it’s trivial to customise each unique error that your initialiser/method can produce.

Consider reading Joe Duffy's writeup on error handling in Midori <Joe Duffy - The Error Model; which presents "abandonments" (what I think failable initializers model best) and actual exceptions (what I think throwing models best).

It’s definitely an interesting article but there are a few drawbacks listed to exceptions that I’d like to go over:

1. Exceptions are used to communicate unrecoverable bugs, like null dereferences, divide-by-zero, etc.

This is really a matter of convention, and also more of a matter of terminology. For example, in Swift, they’re just errors rather than exceptions; i.e- there’s no strict indication of their severity, and like any good “exception” model it’s up to the developer if they want to throw an error onwards, silently hide it, do some recovery etc. If you wish to throw a minor error vs a severe one, then you use different error types and document what they represent.

I’d also argue that a failable initialiser represents no less severe an issue than an error does, as the initialiser has still failed, and therefore created nothing usable, ultimately requiring some form of recovery (testing for nil, supplying a default etc.). It isn’t clear at all to me that these are different things, as both can be recoverable or unrecoverable depending upon which form you use it in (try? vs try! vs try/catch, or Foo() vs Foo()!).

3. Although signatures declare exception types, there is no indication at callsites what calls might throw.

This of course isn’t an issue in Swift, as the try keyword is mandatory. Thanks to try? and try! this also eliminates number four (everybody hates exceptions) which is largely a complaint about having to build try/catch blocks around them, but with try? and try! we can easily eliminate that when we know we don’t need any extra information besides whether or not a failure occurred.

Also, regarding overhead of exceptions, I still think that this is something that can be optimised away. All the compiler has to do is identify throw statements, and isolate code specific to them (i.e- variables etc. that are only used as part of a throw); now when you call a method/initialiser with try? or try!, any code identified as being part of the throw can be skipped as all that’s needed is a binary flag (threw vs. succeeded). Of course it sounds a bit easier than it is in reality, but I’m pretty sure it should be possible to change behaviour depending upon how the code is called (i.e- whether an error will actually be used, or simply ignored in favour of its status). When debugging of course the error can be returned in all cases so we can generate and see it.
In short, with Swift’s ability to explicitly declare whether we want an error returned vs simply knowing that an error occurred, it should be possible to optimise such that it is just as efficient as a failable initialiser (if not more so, since the return value isn’t an optional).

···

On 4 Mar 2016, at 16:32, Félix Cloutier <felixcca@yahoo.ca> wrote: