Idea: Properties in Failable Initializers less verbose

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
	guard
		let someProperty = data["some_key"],
		let anotherProperty = data["another_key"]
	else {
		return nil
	}

	self. someProperty = someProperty
	self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the case of `if let`) making the initializer twice as long as necessary and becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
  init?(data: [String: Any]) {
    guard
      someProperty = data["some_key"], // Currently fails because `self` us used before all stored properties are initialized
      anotherProperty = data["another_key"]
    else {
      return nil
    }
  }
}

I’d be in favor of this.

···

On Tue, Jul 25, 2017 at 5:44 AM, philohan95 via swift-evolution < swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer
`init?()` is overly verbose and should be shortened down so less
boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
        guard
                let someProperty = data["some_key"],
                let anotherProperty = data["another_key"]
        else {
                return nil
        }

        self. someProperty = someProperty
        self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the
case of `if let`) making the initializer twice as long as necessary and
becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently
fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

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

I'm not convinced new syntax is necessary. You can get pretty close to this
today. First, put this in your project somewhere:

struct NilError: Error { }

func throwNilError<Whatever>() throws -> Whatever {
    throw NilError()
}

Then use do/try/catch to initialize your properties:

class MyObject {
    let someProperty: Any
    let anotherProperty: Any

    init?(data: [String: Any]) {
        do {
            someProperty = try data["some_key"] ?? throwNilError()
            anotherProperty = try data["another_key"] ?? throwNilError()
        } catch {
            return nil
        }
    }
}

It also works for Charles Srstka's example:

let bar: String
if someCondition {
    do { bar = mightReturnOptional() ?? throwNilError() }
    catch { return }
} else {
    bar = wontReturnOptional()
}

···

On Tue, Jul 25, 2017 at 4:44 AM, philohan95 via swift-evolution < swift-evolution@swift.org> wrote:

As you can see we had to use the properties twice (this would also be the
case of `if let`) making the initializer twice as long as necessary and
becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently
fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

This was one of the issues I tried to address in my proposal about factory initializers (based on all previous discussions on that topic).
I’d love to see this happen, but much like the reason why I didn’t push the factory initializers proposal to review, I think now’s not the time for this.
The swift dev team has made it clear that they have very strict priorities and stages regarding the release schedule.
I’m afraid at this time, anything that is not a bug-fix or doesn’t align with the current stage’s priorities will be promptly dismissed.
The sad part is that currently there’s no good way of stashing proposals for future revision, so we just gotta keep these in our heads until the time is right.

···

On Jul 25, 2017, at 12:44 PM, philohan95 via swift-evolution <swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
	guard
		let someProperty = data["some_key"],
		let anotherProperty = data["another_key"]
	else {
		return nil
	}

	self. someProperty = someProperty
	self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the case of `if let`) making the initializer twice as long as necessary and becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
  init?(data: [String: Any]) {
    guard
      someProperty = data["some_key"], // Currently fails because `self` us used before all stored properties are initialized
      anotherProperty = data["another_key"]
    else {
      return nil
    }
  }
}

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

Guard isn’t really the right answer for this, I’d try something like this (where unwrapOrThrow is the obvious generic function you can define yourself):

init?(data: [String: Any]) {
  do {
    self.someProperty = try unwrapOrThrow(data["some_key”])
    self.anotherProperty = try unwrapOrThrow(data["another_key”])
  } catch {
    return nil
  }
}

-Chris

···

On Jul 25, 2017, at 2:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
	guard
		let someProperty = data["some_key"],
		let anotherProperty = data["another_key"]
	else {
		return nil
	}

	self. someProperty = someProperty
	self. anotherProperty = anotherProperty
}

Catching and throwing exceptions is not the same as a controlled guard
fail. It’s also quite verbose and requires extra types to be defined
outside the scope of the initializer.

···

On Tue, Jul 25, 2017 at 5:35 PM, Rob Mayoff via swift-evolution < swift-evolution@swift.org> wrote:

On Tue, Jul 25, 2017 at 4:44 AM, philohan95 via swift-evolution < > swift-evolution@swift.org> wrote:

As you can see we had to use the properties twice (this would also be the
case of `if let`) making the initializer twice as long as necessary and
becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently
fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

I'm not convinced new syntax is necessary. You can get pretty close to
this today. First, put this in your project somewhere:

struct NilError: Error { }

func throwNilError<Whatever>() throws -> Whatever {
    throw NilError()
}

Then use do/try/catch to initialize your properties:

class MyObject {
    let someProperty: Any
    let anotherProperty: Any

    init?(data: [String: Any]) {
        do {
            someProperty = try data["some_key"] ?? throwNilError()
            anotherProperty = try data["another_key"] ?? throwNilError()
        } catch {
            return nil
        }
    }
}

It also works for Charles Srstka's example:

let bar: String
if someCondition {
    do { bar = mightReturnOptional() ?? throwNilError() }
    catch { return }
} else {
    bar = wontReturnOptional()
}

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

+1.

···

On 25. Jul 2017, at 18:30, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

I’d be in favor of this.

On Tue, Jul 25, 2017 at 5:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
        guard
                let someProperty = data["some_key"],
                let anotherProperty = data["another_key"]
        else {
                return nil
        }

        self. someProperty = someProperty
        self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the case of `if let`) making the initializer twice as long as necessary and becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

_______________________________________________
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

Although I have come across this problem as well (particularly when
initializing from JSON), I don't think the solution is to fundamentally
change initialization behavior like this, because 1. ) that is probably
going to break a good deal of existing code and 2. ) I think that the
introduction of the Codable protocol in Swift 4 will eliminate most cases
where this is really a problem.

···

On Wed, 26 Jul 2017 at 02:30 Taylor Swift via swift-evolution < swift-evolution@swift.org> wrote:

I’d be in favor of this.

On Tue, Jul 25, 2017 at 5:44 AM, philohan95 via swift-evolution < > swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer
`init?()` is overly verbose and should be shortened down so less
boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
        guard
                let someProperty = data["some_key"],
                let anotherProperty = data["another_key"]
        else {
                return nil
        }

        self. someProperty = someProperty
        self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the
case of `if let`) making the initializer twice as long as necessary and
becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently
fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

_______________________________________________
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

Is there a reason that a throwing unwrap function/operator isn’t part of the standard library? Seems like it would be handy to be able to have a one-liner for attempting to unwrap and throw if it’s nil. Something like

postfix operator .?!

extension Optional {
  static postfix func .?!(optional: Optional<Wrapped>) throws -> Wrapped {
    switch optional {
    case let .some(wrapped):
      return wrapped
    case .none:
      throw UnwrapError()
    }
  }
}

init?(data: [String: Any]) {
  do {
    self.someProperty = try data["some_key”].?!
    self.anotherProperty = try data["another_key”].?!
  } catch is UnwrapError {
    return nil
  }
}

···

On Jul 26, 2017, at 12:13 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 25, 2017, at 2:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
	guard
		let someProperty = data["some_key"],
		let anotherProperty = data["another_key"]
	else {
		return nil
	}

	self. someProperty = someProperty
	self. anotherProperty = anotherProperty
}

Guard isn’t really the right answer for this, I’d try something like this (where unwrapOrThrow is the obvious generic function you can define yourself):

init?(data: [String: Any]) {
  do {
    self.someProperty = try unwrapOrThrow(data["some_key”])
    self.anotherProperty = try unwrapOrThrow(data["another_key”])
  } catch {
    return nil
  }
}

-Chris

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

I’ll take the opposite stance. Why limit this to initialization? I’d love to be able to guard assignments to *any* variable, whether it’s a property or not:

func foo() {
  let bar: String

  if someCondition {
    guard bar = mightReturnOptional() else {
      return
    }
  } else {
    bar = wontReturnOptional()
  }

  self.doSomethingWith(bar)
}

As opposed to:

func foo() {
  let bar: String

  if someCondition {
    guard let _bar = mightReturnOptional() else {
      return
    }

    bar = _bar
  } else {
    bar = wontReturnOptional()
  }

  self.doSomethingWith(bar)
}

Charles

···

On Jul 25, 2017, at 4:13 PM, Niels Andriesse via swift-evolution <swift-evolution@swift.org> wrote:

Although I have come across this problem as well (particularly when initializing from JSON), I don't think the solution is to fundamentally change initialization behavior like this, because 1. ) that is probably going to break a good deal of existing code and 2. ) I think that the introduction of the Codable protocol in Swift 4 will eliminate most cases where this is really a problem.
On Wed, 26 Jul 2017 at 02:30 Taylor Swift via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I’d be in favor of this.

On Tue, Jul 25, 2017 at 5:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
        guard
                let someProperty = data["some_key"],
                let anotherProperty = data["another_key"]
        else {
                return nil
        }

        self. someProperty = someProperty
        self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be the case of `if let`) making the initializer twice as long as necessary and becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

_______________________________________________
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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This problem is not limited to JSON. I have scads of underscored variables
and guard bindings in my code because of this limitation, though I rarely
work with JSON.

···

On Tue, Jul 25, 2017 at 5:13 PM, Niels Andriesse <andriesseniels@gmail.com> wrote:

Although I have come across this problem as well (particularly when
initializing from JSON), I don't think the solution is to fundamentally
change initialization behavior like this, because 1. ) that is probably
going to break a good deal of existing code and 2. ) I think that the
introduction of the Codable protocol in Swift 4 will eliminate most cases
where this is really a problem.

On Wed, 26 Jul 2017 at 02:30 Taylor Swift via swift-evolution < > swift-evolution@swift.org> wrote:

I’d be in favor of this.

On Tue, Jul 25, 2017 at 5:44 AM, philohan95 via swift-evolution < >> swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer
`init?()` is overly verbose and should be shortened down so less
boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
        guard
                let someProperty = data["some_key"],
                let anotherProperty = data["another_key"]
        else {
                return nil
        }

        self. someProperty = someProperty
        self. anotherProperty = anotherProperty
}

As you can see we had to use the properties twice (this would also be
the case of `if let`) making the initializer twice as long as necessary and
becomes a pain to implement when having more than 1 property.

My idea is extending the power of the `guard` statement

Idea:
        init?(data: [String: Any]) {
                guard
                        someProperty = data["some_key"], // Currently
fails because `self` us used before all stored properties are initialized
                        anotherProperty = data["another_key"]
                else {
                        return nil
                }
        }
}

_______________________________________________
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

Is there a reason that a throwing unwrap function/operator isn’t part of the standard library? Seems like it would be handy to be able to have a one-liner for attempting to unwrap and throw if it’s nil.

?! would be the wrong name, since ! is generally reserved for potentially-trapping operations that should be avoided in most cases.

If you’re going to explore this branch of the design tree, the sensible thing seems to be to (conceptually) carve off some chunk of the operator space for throwing operators (e.g. ^ which could be rationalized as connoting “raising” an error). This would argue for postfix ^ to unwrap-or-throw (as an analog to postfix ?, enabling chaining etc), and would then provide a schema to define other “or throw” operations.

-Chris

···

On Jul 26, 2017, at 9:55 AM, Robert Bennett <rltbennett@icloud.com> wrote:

Something like

postfix operator .?!

extension Optional {
  static postfix func .?!(optional: Optional<Wrapped>) throws -> Wrapped {
    switch optional {
    case let .some(wrapped):
      return wrapped
    case .none:
      throw UnwrapError()
    }
  }
}

init?(data: [String: Any]) {
  do {
    self.someProperty = try data["some_key”].?!
    self.anotherProperty = try data["another_key”].?!
  } catch is UnwrapError {
    return nil
  }
}

On Jul 26, 2017, at 12:13 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 25, 2017, at 2:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
	guard
		let someProperty = data["some_key"],
		let anotherProperty = data["another_key"]
	else {
		return nil
	}

	self. someProperty = someProperty
	self. anotherProperty = anotherProperty
}

Guard isn’t really the right answer for this, I’d try something like this (where unwrapOrThrow is the obvious generic function you can define yourself):

init?(data: [String: Any]) {
  do {
    self.someProperty = try unwrapOrThrow(data["some_key”])
    self.anotherProperty = try unwrapOrThrow(data["another_key”])
  } catch {
    return nil
  }
}

-Chris

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

Makes sense. Off the top of my head, I can imagine the following two operators:

`optional?^` throws some default error if nil
`optional ??^ error` throws `error` if nil

You could have optional throwing chaining as well — basically everywhere the language currently offers a choice of graceful optional or potentially fatal force unwrap, there could be a third option to propagate an error. Is this worth exploring further in a new thread? I feel like this has been discussed and decided against...

···

On Jul 26, 2017, at 4:01 PM, Chris Lattner <clattner@nondot.org> wrote:

On Jul 26, 2017, at 9:55 AM, Robert Bennett <rltbennett@icloud.com> wrote:

Is there a reason that a throwing unwrap function/operator isn’t part of the standard library? Seems like it would be handy to be able to have a one-liner for attempting to unwrap and throw if it’s nil.

?! would be the wrong name, since ! is generally reserved for potentially-trapping operations that should be avoided in most cases.

If you’re going to explore this branch of the design tree, the sensible thing seems to be to (conceptually) carve off some chunk of the operator space for throwing operators (e.g. ^ which could be rationalized as connoting “raising” an error). This would argue for postfix ^ to unwrap-or-throw (as an analog to postfix ?, enabling chaining etc), and would then provide a schema to define other “or throw” operations.

-Chris

Something like

postfix operator .?!

extension Optional {
   static postfix func .?!(optional: Optional<Wrapped>) throws -> Wrapped {
       switch optional {
       case let .some(wrapped):
           return wrapped
       case .none:
           throw UnwrapError()
       }
   }
}

init?(data: [String: Any]) {
   do {
       self.someProperty = try data["some_key”].?!
       self.anotherProperty = try data["another_key”].?!
   } catch is UnwrapError {
       return nil
   }
}

On Jul 26, 2017, at 12:13 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jul 25, 2017, at 2:44 AM, philohan95 via swift-evolution <swift-evolution@swift.org> wrote:

I think the current way to initiate models in a Failable Initializer `init?()` is overly verbose and should be shortened down so less boilerplate should be needed.

The current way:

let someProperty: Any
let anotherProperty: Any

init?(data: [String: Any]) {
   guard
       let someProperty = data["some_key"],
       let anotherProperty = data["another_key"]
   else {
       return nil
   }

   self. someProperty = someProperty
   self. anotherProperty = anotherProperty
}

Guard isn’t really the right answer for this, I’d try something like this (where unwrapOrThrow is the obvious generic function you can define yourself):

init?(data: [String: Any]) {
   do {
       self.someProperty = try unwrapOrThrow(data["some_key”])
       self.anotherProperty = try unwrapOrThrow(data["another_key”])
   } catch {
       return nil
   }
}

-Chris

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

Terms of Service

Privacy Policy

Cookie Policy