Remove Failable Initializers


(James Campbell) #1

Given that we now have error handling and availability checks does it make
sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON
structure and returns an i.

We could guarantee that it always returns an instance but then we have to
populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing
information but I would be making the structure mutable which adds
complexity. On-top of that I wouldn't be able to tell if the property is
nil due to a lack of a value or a bug causing certain JSON information to
be missing.

So lets look at the alternatives with a non-guranteed system, well we
have Failable Initializers. If any of the properties are missing, we should
return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer
unless swift was update to, meaning we may forget to handle the nil case:

*MyModel?(json)*

This isn't all that is wrong with this approach, if this method does a lot
of steps to create the object and fails (Like parsing an object from JSON).
Returning nil but we have no idea why, it makes it easy to introduce
silently failing errors.

Its true some classes may do this when ran on a older version of iOS but
with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue
with this corrupted data - and the error will be informative as it forces
us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can
still use *try? *(For example in a Chat application you could ignore
corrupted messages).
- In these cases where we ignore the error using *try?* it makes it super
obvious we are ignoring an error and it should be easy to that error
causing it to fail by using *try! *
- On the whole it encourages us to not return nil and to return useful
error codes which we can handle, what if one of the reasons it failed to
parse is something we could recover from? If we return nil we will never
know.
- And it reduces the amount of mutability in swift.

···

*___________________________________*

*James⎥Head of Trolls*

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

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *


(Chris Lattner) #2

If I understand correctly, your aim is to simplify the language. I understand and support that goal generally, but have concerns in this specific case.

Our error handling schema (https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error) defines how error conditions are handled, and one important class of them (e.g. the "string to int" case) is best modeled as returning an optional. This works really well in practice for functions/methods in general.

The problem is that removing failable initializers would simplify initializers, but would add complexity that would drive a wedge between initializers and functions, the end result being that a lot of initializers would have to become static functions on types. I don’t think that everyone would just use error handling.

-Chris

···

On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?


(Haravikk) #3

I haven’t used a single failable initialiser ever since the try! command was added, as that gave the same convenience as force unwrapping and eliminated the need for big try/catch blocks if you didn’t expect an exception, with try? likewise making it easy to detect that any exception occurred, without caring about what it was.

So I’d probably say that failable initialisers are unnecessary now; they’re a tiny bit cleaner for simpler failable initialisers (one type of failure), but it’s probably not worth keeping as a language feature when it comes up so rarely (at least in my experience).

···

On 2 Mar 2016, at 21:11, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON structure and returns an i.

We could guarantee that it always returns an instance but then we have to populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing information but I would be making the structure mutable which adds complexity. On-top of that I wouldn't be able to tell if the property is nil due to a lack of a value or a bug causing certain JSON information to be missing.

So lets look at the alternatives with a non-guranteed system, well we have Failable Initializers. If any of the properties are missing, we should return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer unless swift was update to, meaning we may forget to handle the nil case:

MyModel?(json)

This isn't all that is wrong with this approach, if this method does a lot of steps to create the object and fails (Like parsing an object from JSON). Returning nil but we have no idea why, it makes it easy to introduce silently failing errors.

Its true some classes may do this when ran on a older version of iOS but with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue with this corrupted data - and the error will be informative as it forces us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can still use try? (For example in a Chat application you could ignore corrupted messages).
- In these cases where we ignore the error using try? it makes it super obvious we are ignoring an error and it should be easy to that error causing it to fail by using try!
- On the whole it encourages us to not return nil and to return useful error codes which we can handle, what if one of the reasons it failed to parse is something we could recover from? If we return nil we will never know.
- And it reduces the amount of mutability in swift.

___________________________________

James⎥Head of Trolls

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

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


(Haravikk) #4

Could you give an example of why failable is the better fit here? To me the following two statements are identical:

  let a = FailableType()
  let b = try? ThrowableType()

Except that in the latter case the try? is more explicit about what is happening (and that it can fail), and I have the option of catching the error to find out more about what went wrong. With some optimisation it should be possible for try? to be just as efficient as a failable initialiser I think.

That said, the failable initialiser could have the same explicit call syntax if it required a trailing question-mark, e.g:

  let a = FailableType()?

As currently the only indicator is on the initialiser declaration itself. Still, when it comes to debugging I’ve found it very useful to force myself to use error handling instead, as it means I have to give reasons for why something failed, which can make it easier to track issues when they do arise.

···

On 2 Mar 2016, at 23:07, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Our error handling schema (https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error) defines how error conditions are handled, and one important class of them (e.g. the "string to int" case) is best modeled as returning an optional. This works really well in practice for functions/methods in general.


(Radek Pietruszewski) #5

-1.

I think throwing initializers are great, but failable initializers are useful too — in the same way throwing errors is great, but sometimes all you want is an optional.

I understand where you’re coming from, but I don’t feel comfortable about the idea of Swift forcing me to throw a dummy error when I don’t care about one, or having to use a special factory method to accomplish what `init?` does very well today.

— Radek

···

On 02 Mar 2016, at 22:11, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON structure and returns an i.

We could guarantee that it always returns an instance but then we have to populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing information but I would be making the structure mutable which adds complexity. On-top of that I wouldn't be able to tell if the property is nil due to a lack of a value or a bug causing certain JSON information to be missing.

So lets look at the alternatives with a non-guranteed system, well we have Failable Initializers. If any of the properties are missing, we should return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer unless swift was update to, meaning we may forget to handle the nil case:

MyModel?(json)

This isn't all that is wrong with this approach, if this method does a lot of steps to create the object and fails (Like parsing an object from JSON). Returning nil but we have no idea why, it makes it easy to introduce silently failing errors.

Its true some classes may do this when ran on a older version of iOS but with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue with this corrupted data - and the error will be informative as it forces us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can still use try? (For example in a Chat application you could ignore corrupted messages).
- In these cases where we ignore the error using try? it makes it super obvious we are ignoring an error and it should be easy to that error causing it to fail by using try!
- On the whole it encourages us to not return nil and to return useful error codes which we can handle, what if one of the reasons it failed to parse is something we could recover from? If we return nil we will never know.
- And it reduces the amount of mutability in swift.

___________________________________

James⎥Head of Trolls

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

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


(Patrick Gili) #6

-1 for me. I like the flexibility of having both failable and throwable initializers.

···

On Mar 2, 2016, at 4:11 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON structure and returns an i.

We could guarantee that it always returns an instance but then we have to populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing information but I would be making the structure mutable which adds complexity. On-top of that I wouldn't be able to tell if the property is nil due to a lack of a value or a bug causing certain JSON information to be missing.

So lets look at the alternatives with a non-guranteed system, well we have Failable Initializers. If any of the properties are missing, we should return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer unless swift was update to, meaning we may forget to handle the nil case:

MyModel?(json)

This isn't all that is wrong with this approach, if this method does a lot of steps to create the object and fails (Like parsing an object from JSON). Returning nil but we have no idea why, it makes it easy to introduce silently failing errors.

Its true some classes may do this when ran on a older version of iOS but with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue with this corrupted data - and the error will be informative as it forces us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can still use try? (For example in a Chat application you could ignore corrupted messages).
- In these cases where we ignore the error using try? it makes it super obvious we are ignoring an error and it should be easy to that error causing it to fail by using try!
- On the whole it encourages us to not return nil and to return useful error codes which we can handle, what if one of the reasons it failed to parse is something we could recover from? If we return nil we will never know.
- And it reduces the amount of mutability in swift.

___________________________________

James⎥Head of Trolls

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

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


(Tino) #7

+1
Without "try?", it would be really inconvenient to not have "init?" — but failable initializers as they are now are somewhat odd, because they are half-function and half-procedure:
Regular init-methods have no return, so you can basically think of them as a configuration that is called on an allocated object.
This isn't true anymore for "init?", as it not only it turns a "void-function" into something that returns an optional, but also doesn't explicitly model the non-nil case (there is no "return self").
Replacing this mechanism with an error would actually make initializers more method-like, and less special.

Tino


(Tino) #8

As ease of use and performance implications have been discussed, I would still like to see the question of consistency addressed:
In Objective-C, returning nil from an initializer is nothing special — it is a regular function that returns id (self in most cases).

Swift, on the other hand, is different:
Init-methods are void-functions in every aspect, with the irregularity that we can return "nil" (if the initializer is marked accordingly).

Am I the only one who has the feeling that this is a little bit odd?
What kind of method is a initializer in Swift?

Tino


(Kyle Sherman) #9

It seems to me that Haravikk’s example shows that you can handle the case of “string to int” with the error handling model just fine instead of having the failable initializers. I have found that failable initializers really don’t fit in well with Swift’s strict initialization rules (which I think are great). Every time I tried to use them I always had to make sacrifices with having an implicitly unwrapped optional or optional when I shouldn’t.

I think using the error handling model in combination with try? would be simpler and would lead to better code.

···

> On 2 Mar 2016, at 23:07, Chris Lattner via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:
> On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:
> >
> > Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?
>
> Our error handling schema (https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error) defines how error conditions are handled, and one important class of them (e.g. the "string to int" case) is best modeled as returning an optional.This works really well in practice for functions/methods in general.
Could you give an example of why failable is the better fit here? To me the following two statements are identical:

let a = FailableType()
let b = try? ThrowableType()

Except that in the latter case the try? is more explicit about what is happening (and that it can fail), and I have the option of catching the error to find out more about what went wrong. With some optimisation it should be possible for try? to be just as efficient as a failable initialiser I think.

That said, the failable initialiser could have the same explicit call syntax if it required a trailing question-mark, e.g:

let a = FailableType()?

As currently the only indicator is on the initialiser declaration itself. Still, when it comes to debugging I’ve found it very useful to force myself to use error handling instead, as it means I have to give reasons for why something failed, which can make it easier to track issues when they do arise._______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ross O'Brien) #10

At the risk of appearing glib or naive - which isn't my intention, I'd like
to know the answer - is there not a similar argument to be made for any
function which returns an optional instead of throwing a more descriptive
error? Asking an array for its first element returns an optional because of
the possibility it might have no elements in it; should this throw an error
instead of being 'failable'?

···

On Wed, Mar 2, 2016 at 11:35 PM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

On 2 Mar 2016, at 23:07, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make
sense to have Failable Initializers which date back to Swift 1.1?

Our error handling schema (
https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error)
defines how error conditions are handled, and one important class of them
(e.g. the "string to int" case) is best modeled as returning an optional.
This works really well in practice for functions/methods in general.

Could you give an example of why failable is the better fit here? To me
the following two statements are identical:

let a = FailableType()
let b = try? ThrowableType()

Except that in the latter case the try? is more explicit about what is
happening (and that it can fail), and I have the option of catching the
error to find out more about what went wrong. With some optimisation it
should be possible for try? to be just as efficient as a failable
initialiser I think.

That said, the failable initialiser could have the same explicit call
syntax if it required a trailing question-mark, e.g:

let a = FailableType()?

As currently the only indicator is on the initialiser declaration itself.
Still, when it comes to debugging I’ve found it very useful to force myself
to use error handling instead, as it means I have to give reasons for why
something failed, which can make it easier to track issues when they do
arise.

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


(Brent Royal-Gordon) #11

I have found that failable initializers really don’t fit in well with Swift’s strict initialization rules (which I think are great). Every time I tried to use them I always had to make sacrifices with having an implicitly unwrapped optional or optional when I shouldn’t.

Are you talking about the "All stored properties of a class instance must be initialized before returning nil from an initializer" error? Because that's being fixed in Swift 2.2.

···

--
Brent Royal-Gordon
Architechies


(Howard Lovatt) #12

Not that I personally use failable initializers, but aren't they needed to
interact with Obj-C which returns nil to indicate failure from initializers?

  -- Howard.

···

On 3 March 2016 at 09:27, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

I haven’t used a single failable initialiser ever since the try! command
was added, as that gave the same convenience as force unwrapping and
eliminated the need for big try/catch blocks if you didn’t expect an
exception, with try? likewise making it easy to detect that any exception
occurred, without caring about what it was.

So I’d probably say that failable initialisers are unnecessary now;
they’re a tiny bit cleaner for simpler failable initialisers (one type of
failure), but it’s probably not worth keeping as a language feature when it
comes up so rarely (at least in my experience).

On 2 Mar 2016, at 21:11, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it make
sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON
structure and returns an i.

We could guarantee that it always returns an instance but then we have to
populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was
missing information but I would be making the structure mutable which adds
complexity. On-top of that I wouldn't be able to tell if the property is
nil due to a lack of a value or a bug causing certain JSON information to
be missing.

So lets look at the alternatives with a non-guranteed system, well we
have Failable Initializers. If any of the properties are missing, we should
return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer
unless swift was update to, meaning we may forget to handle the nil case:

*MyModel?(json)*

This isn't all that is wrong with this approach, if this method does a lot
of steps to create the object and fails (Like parsing an object from JSON).
Returning nil but we have no idea why, it makes it easy to introduce
silently failing errors.

Its true some classes may do this when ran on a older version of iOS but
with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue
with this corrupted data - and the error will be informative as it forces
us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can
still use *try? *(For example in a Chat application you could ignore
corrupted messages).
- In these cases where we ignore the error using *try?* it makes it super
obvious we are ignoring an error and it should be easy to that error
causing it to fail by using *try! *
- On the whole it encourages us to not return nil and to return useful
error codes which we can handle, what if one of the reasons it failed to
parse is something we could recover from? If we return nil we will never
know.
- And it reduces the amount of mutability in swift.

*___________________________________*

*James⎥Head of Trolls*

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

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
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


(Charles Kissinger) #13

-1.

I think throwing initializers are great, but failable initializers are useful too — in the same way throwing errors is great, but sometimes all you want is an optional.

I understand where you’re coming from, but I don’t feel comfortable about the idea of Swift forcing me to throw a dummy error when I don’t care about one, or having to use a special factory method to accomplish what `init?` does very well today.

I agree. This is a simplification that is actually a complication in my mind. It makes initializers a special case for error handling where they weren’t before.

—CK

···

On Mar 3, 2016, at 1:24 AM, Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> wrote:

— Radek

On 02 Mar 2016, at 22:11, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON structure and returns an i.

We could guarantee that it always returns an instance but then we have to populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing information but I would be making the structure mutable which adds complexity. On-top of that I wouldn't be able to tell if the property is nil due to a lack of a value or a bug causing certain JSON information to be missing.

So lets look at the alternatives with a non-guranteed system, well we have Failable Initializers. If any of the properties are missing, we should return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer unless swift was update to, meaning we may forget to handle the nil case:

MyModel?(json)

This isn't all that is wrong with this approach, if this method does a lot of steps to create the object and fails (Like parsing an object from JSON). Returning nil but we have no idea why, it makes it easy to introduce silently failing errors.

Its true some classes may do this when ran on a older version of iOS but with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue with this corrupted data - and the error will be informative as it forces us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can still use try? (For example in a Chat application you could ignore corrupted messages).
- In these cases where we ignore the error using try? it makes it super obvious we are ignoring an error and it should be easy to that error causing it to fail by using try!
- On the whole it encourages us to not return nil and to return useful error codes which we can handle, what if one of the reasons it failed to parse is something we could recover from? If we return nil we will never know.
- And it reduces the amount of mutability in swift.

___________________________________

James⎥Head of Trolls

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>

10 Finsbury Square

London

>> EC2A 1AF

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

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


(James Campbell) #14

This is my draft proposal.

https://github.com/jcampbell05/swift-evolution/blob/master/proposals/0045-remove-falliable-initilizer.md

Let me know your thoughts.

···

*___________________________________*

*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 Mon, Mar 7, 2016 at 2:27 PM, Tino Heth <2th@gmx.de> wrote:

+1
Without "try?", it would be really inconvenient to not have "init?" — but
failable initializers as they are now are somewhat odd, because they are
half-function and half-procedure:
Regular init-methods have no return, so you can basically think of them as
a configuration that is called on an allocated object.
This isn't true anymore for "init?", as it not only it turns a
"void-function" into something that returns an optional, but also doesn't
explicitly model the non-nil case (there is no "return self").
Replacing this mechanism with an error would actually make initializers
more method-like, and less special.

Tino


(Haravikk) #15

As ease of use and performance implications have been discussed, I would still like to see the question of consistency addressed:
In Objective-C, returning nil from an initializer is nothing special — it is a regular function that returns id (self in most cases).

Swift, on the other hand, is different:
Init-methods are void-functions in every aspect, with the irregularity that we can return "nil" (if the initializer is marked accordingly).

Am I the only one who has the feeling that this is a little bit odd?
What kind of method is a initializer in Swift?

A related consideration to this is that we have no choice in how initialisers are named. For example, a method named “search” is pretty clear that it will look for something, but places no guarantee that a result will actually be found, which means that it’s fairly logical that it has an optional return type vs an error (since finding nothing isn’t an error, just one of the possible outcomes).

Initialisers meanwhile are really named for the type they belong to; though we can use more descriptive parameter names I suppose to give the implication of optionality, it can still be messy when you’re dealing with a mixture of regular and failable initialisers that all look very similar. Requiring a trailing question mark to indicate optionality at the call site would partially address this, though the feature still seems redundant to me.

···

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

On 9 Mar 2016, at 06:23, Thorsten Seitz <tseitz42@icloud.com> wrote:

If I have a simple case where I just want to skip failed initializations the errors won't help me anything in unexpected cases because I disregarded them anyway.

You can only guarantee that they’ll be universally disregarded if you’re talking about an internal or private type, in which case you could just as easily throw anything you like if you know you’re never going to actually use it; personally I’d still try to throw something semi-useful, even if it just ends functioning as a useful reminder it’s better than nothing.

In terms of maintainability it can also be useful to other developers even if they have access to the source code of your initialiser, as instead of having to dig into that to find out what’s wrong they can try inspecting the error first to see if that helps; I don’t think we can currently access the error that was thrown into a try? statement, but if the debugger could do this it’d be even easier.


(Jens Persson) #16

Swift, on the other hand, is different:
Init-methods are void-functions in every aspect, with the irregularity
that we can return "nil" (if the initializer is marked accordingly).

Initializers are clearly not void-functions, as they return a value of type
T.
And failable initializers return an Optional<T>.
Where T is the type to which the initializer belongs.

If initializers where void-functions in every aspect, how would we explain
what happens here:
let a: Int = Int.init(123)
let b: Int? = Int.init("ff", radix: 16)
print(a) // 123
print(b) // 255
?

/Jens

···

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

As ease of use and performance implications have been discussed, I would
still like to see the question of consistency addressed:
In Objective-C, returning nil from an initializer is nothing special — it
is a regular function that returns id (self in most cases).

Swift, on the other hand, is different:
Init-methods are void-functions in every aspect, with the irregularity
that we can return "nil" (if the initializer is marked accordingly).

Am I the only one who has the feeling that this is a little bit odd?
What kind of method is a initializer in Swift?

Tino

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

--
bitCycle AB | Smedjegatan 12 | 742 32 Östhammar | Sweden
http://www.bitcycle.com/
Phone: +46-73-753 24 62
E-mail: jens@bitcycle.com


(Jens Persson) #17

What kind of method is a initializer in Swift?

I would say initializers are very similar to static methods taking whatever
arguments and returning either T or Optional(T), depending on whether they
are failable or not, where T is their enclosing type.

let fnTypeA: (Int) -> Int = { Int.init($0) }
let fnTypeB: (String, Int) -> Int? = Int.init
let a = fnTypeA(123)
let b = fnTypeB("ff", 16)
print(a) // 123
print(b) // Optional(255)
print(fnTypeA.dynamicType) // Int -> Int
print(fnTypeB.dynamicType) // (String, Int) -> Optional<Int>

···

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

As ease of use and performance implications have been discussed, I would
still like to see the question of consistency addressed:
In Objective-C, returning nil from an initializer is nothing special — it
is a regular function that returns id (self in most cases).

Swift, on the other hand, is different:
Init-methods are void-functions in every aspect, with the irregularity
that we can return "nil" (if the initializer is marked accordingly).

Am I the only one who has the feeling that this is a little bit odd?
What kind of method is a initializer in Swift?

Tino

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

--
bitCycle AB | Smedjegatan 12 | 742 32 Östhammar | Sweden
http://www.bitcycle.com/
Phone: +46-73-753 24 62
E-mail: jens@bitcycle.com


(Austin Zheng) #18

+1. Keep failable initializers to maintain the existing symmetry with functions, and to handle cases in which an initialization operation can only 'fail' in one way that users care about (e.g. the 'int-from-string' initializer; either the string contains a representable integer or it doesn't).

Austin

···

On Mar 2, 2016, at 3:44 PM, Ross O'Brien via swift-evolution <swift-evolution@swift.org> wrote:

At the risk of appearing glib or naive - which isn't my intention, I'd like to know the answer - is there not a similar argument to be made for any function which returns an optional instead of throwing a more descriptive error? Asking an array for its first element returns an optional because of the possibility it might have no elements in it; should this throw an error instead of being 'failable'?

On Wed, Mar 2, 2016 at 11:35 PM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Mar 2016, at 23:07, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Our error handling schema (https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error) defines how error conditions are handled, and one important class of them (e.g. the "string to int" case) is best modeled as returning an optional. This works really well in practice for functions/methods in general.

Could you give an example of why failable is the better fit here? To me the following two statements are identical:

  let a = FailableType()
  let b = try? ThrowableType()

Except that in the latter case the try? is more explicit about what is happening (and that it can fail), and I have the option of catching the error to find out more about what went wrong. With some optimisation it should be possible for try? to be just as efficient as a failable initialiser I think.

That said, the failable initialiser could have the same explicit call syntax if it required a trailing question-mark, e.g:

  let a = FailableType()?

As currently the only indicator is on the initialiser declaration itself. Still, when it comes to debugging I’ve found it very useful to force myself to use error handling instead, as it means I have to give reasons for why something failed, which can make it easier to track issues when they do arise.

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

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


(Haravikk) #19

If the feature were being removed, then that could be replaced by some default exception for that purpose I think?

···

On 2 Mar 2016, at 22:30, Howard Lovatt <howard.lovatt@gmail.com> wrote:

Not that I personally use failable initializers, but aren't they needed to interact with Obj-C which returns nil to indicate failure from initializers?

  -- Howard.

On 3 March 2016 at 09:27, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I haven’t used a single failable initialiser ever since the try! command was added, as that gave the same convenience as force unwrapping and eliminated the need for big try/catch blocks if you didn’t expect an exception, with try? likewise making it easy to detect that any exception occurred, without caring about what it was.

So I’d probably say that failable initialisers are unnecessary now; they’re a tiny bit cleaner for simpler failable initialisers (one type of failure), but it’s probably not worth keeping as a language feature when it comes up so rarely (at least in my experience).

On 2 Mar 2016, at 21:11, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Given that we now have error handling and availability checks does it make sense to have Failable Initializers which date back to Swift 1.1?

Take this model

struct MyModel {

let someProperty
let anotherProperty
}

Lets say we have a Initializers for this structure that takes a JSON structure and returns an i.

We could guarantee that it always returns an instance but then we have to populate this data with dummy values if the JSON is missing values.

I could make these properties optional to reflect that the JSON was missing information but I would be making the structure mutable which adds complexity. On-top of that I wouldn't be able to tell if the property is nil due to a lack of a value or a bug causing certain JSON information to be missing.

So lets look at the alternatives with a non-guranteed system, well we have Failable Initializers. If any of the properties are missing, we should return nil.

Well we have this issue:

MyModel(json)

It isn't clear that this is fallable compared to a traditional initializer unless swift was update to, meaning we may forget to handle the nil case:

MyModel?(json)

This isn't all that is wrong with this approach, if this method does a lot of steps to create the object and fails (Like parsing an object from JSON). Returning nil but we have no idea why, it makes it easy to introduce silently failing errors.

Its true some classes may do this when ran on a older version of iOS but with avaliabilty checks, this use case is irrelevant.

So what about throwing an error?

It forces us to handle it failing to initialize for some reason.

- If we forget to handle the error, the app won't be allowed to continue with this corrupted data - and the error will be informative as it forces us to create an ErrorType.
- If we want to ignore this object and convert the error to nil we can still use try? (For example in a Chat application you could ignore corrupted messages).
- In these cases where we ignore the error using try? it makes it super obvious we are ignoring an error and it should be easy to that error causing it to fail by using try!
- On the whole it encourages us to not return nil and to return useful error codes which we can handle, what if one of the reasons it failed to parse is something we could recover from? If we return nil we will never know.
- And it reduces the amount of mutability in swift.

___________________________________

James⎥Head of Trolls

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>

10 Finsbury Square

London

>> EC2A 1AF

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

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


(James Campbell) #20

To me it depends on if the function returns a value or the result of an
action which may have different results not related to the value.

- The first function on an array returns a value, the first item in an
array. In this case an optional makes sense as its easy to handle by
returning nil since there is no value that exists at index(1)
- The subscript operator could return an optional too when we are out of
bounds since the value doesn't exist.
- Having a init throw an error makes sense to me, as for complex structures
and classes there are many reasons it can fail, its not that simple to say
there wasn't a value (was there some internal validation that failed)
- A fetch operation for a network request would throw on the same princible
(i.e was networking down?)
- However a function that just calculated a value lets say the next URL for
the next page in a eBook, would probably want too return nil since all its
doing is returning a value (i.e there is a next page for this book).

···

*___________________________________*

*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 2, 2016 at 11:44 PM, Ross O'Brien via swift-evolution < swift-evolution@swift.org> wrote:

At the risk of appearing glib or naive - which isn't my intention, I'd
like to know the answer - is there not a similar argument to be made for
any function which returns an optional instead of throwing a more
descriptive error? Asking an array for its first element returns an
optional because of the possibility it might have no elements in it; should
this throw an error instead of being 'failable'?

On Wed, Mar 2, 2016 at 11:35 PM, Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

On 2 Mar 2016, at 23:07, Chris Lattner via swift-evolution < >> swift-evolution@swift.org> wrote:

On Mar 2, 2016, at 1:11 PM, James Campbell via swift-evolution < >> swift-evolution@swift.org> wrote:

Given that we now have error handling and availability checks does it
make sense to have Failable Initializers which date back to Swift 1.1?

Our error handling schema (
https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#kinds-of-error)
defines how error conditions are handled, and one important class of them
(e.g. the "string to int" case) is best modeled as returning an optional.
This works really well in practice for functions/methods in general.

Could you give an example of why failable is the better fit here? To me
the following two statements are identical:

let a = FailableType()
let b = try? ThrowableType()

Except that in the latter case the try? is more explicit about what is
happening (and that it can fail), and I have the option of catching the
error to find out more about what went wrong. With some optimisation it
should be possible for try? to be just as efficient as a failable
initialiser I think.

That said, the failable initialiser could have the same explicit call
syntax if it required a trailing question-mark, e.g:

let a = FailableType()?

As currently the only indicator is on the initialiser declaration itself.
Still, when it comes to debugging I’ve found it very useful to force myself
to use error handling instead, as it means I have to give reasons for why
something failed, which can make it easier to track issues when they do
arise.

_______________________________________________
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