[proposal] Move @noescape and @autoclosure to type attributes


(Chris Lattner) #1

Introduction

This proposal suggests moving the existing @noescape and @autoclosure attributes from being declaration attributes on a parameter to being type attributes. This improves consistency and reduces redundancy within the language, e.g. aligning with the previous Swift 3 decision to move “inout”, and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

Motivation

Chris Eidhof noticed an emergent result of removing our currying syntax: it broke some useful code using @noescape, because we only allowed it on parameter declarations, not on general things-of-function-type. This meant that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd), but required @noescape being allowed on arbitrary function types. Now that we have that, these two declarations are equivalent:

  func f(@noescape fn : () -> ()) {} // declaration attribute
  func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less pressing) has the exact same problem. That said, it is currently in a worse place than @noescape because you cannot actually spell the type of a function that involves it. Consider an autoclosure-taking function like this:

  func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

  f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

  let x = f2
  x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can see this if you force a type error:

  let y : Int = x // error: cannot convert value of type '(@autoclosure () -> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

  let x2 : (@autoclosure () -> ()) -> () = f2
  // error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the language, and seems silly that you can use type inference but not manual specification for the declaration of x2.

Proposed solution

The solution solution is straight-forward: disallow @noescape and @autoclosure on declarations, and instead require them on the types. This means that only the type-attribute syntax is supported:

  func f(fn : @noescape () -> ()) {} // type attribute.
  func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is “(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure () -> ()) -> ()”. This fixes the problem with x2, and eliminates the redundancy between the @noescape forms.

Impact on existing code

This breaks existing code that uses these in the old position, so it would be great to roll this out with the other disruptive changes happening in Swift 3. The Swift 3 migrator should move these over, and has the information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source incompatible with Swift 3 code in general, it seems best to make these a hard error in the final Swift 3 release. It would make sense to have a deprecation warning period for swift.org projects like corelibs and swiftpm, and other open source users tracking the public releases though.

-Chris


(Pierre Monod-Broca) #2

It makes sense, and worth the change, IMHO.

Pierre

···

Le 10 mars 2016 à 06:38, Chris Lattner via swift-evolution <swift-evolution@swift.org> a écrit :

Introduction

This proposal suggests moving the existing @noescape and @autoclosure attributes from being declaration attributes on a parameter to being type attributes. This improves consistency and reduces redundancy within the language, e.g. aligning with the previous Swift 3 decision to move “inout”, and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

Motivation

Chris Eidhof noticed an emergent result of removing our currying syntax: it broke some useful code using @noescape, because we only allowed it on parameter declarations, not on general things-of-function-type. This meant that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd), but required @noescape being allowed on arbitrary function types. Now that we have that, these two declarations are equivalent:

  func f(@noescape fn : () -> ()) {} // declaration attribute
  func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less pressing) has the exact same problem. That said, it is currently in a worse place than @noescape because you cannot actually spell the type of a function that involves it. Consider an autoclosure-taking function like this:

  func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

  f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

  let x = f2
  x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can see this if you force a type error:

  let y : Int = x // error: cannot convert value of type '(@autoclosure () -> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

  let x2 : (@autoclosure () -> ()) -> () = f2
  // error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the language, and seems silly that you can use type inference but not manual specification for the declaration of x2.

Proposed solution

The solution solution is straight-forward: disallow @noescape and @autoclosure on declarations, and instead require them on the types. This means that only the type-attribute syntax is supported:

  func f(fn : @noescape () -> ()) {} // type attribute.
  func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is “(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure () -> ()) -> ()”. This fixes the problem with x2, and eliminates the redundancy between the @noescape forms.

Impact on existing code

This breaks existing code that uses these in the old position, so it would be great to roll this out with the other disruptive changes happening in Swift 3. The Swift 3 migrator should move these over, and has the information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source incompatible with Swift 3 code in general, it seems best to make these a hard error in the final Swift 3 release. It would make sense to have a deprecation warning period for swift.org projects like corelibs and swiftpm, and other open source users tracking the public releases though.

-Chris

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


(Adrian Kashivskyy) #3

+1. In my opinion it fits well with recent changes.

Pozdrawiam – Regards,
Adrian Kashivskyy

···

Wiadomość napisana przez Chris Lattner via swift-evolution <swift-evolution@swift.org> w dniu 10.03.2016, o godz. 06:38:

Introduction

This proposal suggests moving the existing @noescape and @autoclosure attributes from being declaration attributes on a parameter to being type attributes. This improves consistency and reduces redundancy within the language, e.g. aligning with the previous Swift 3 decision to move “inout”, and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

Motivation

Chris Eidhof noticed an emergent result of removing our currying syntax: it broke some useful code using @noescape, because we only allowed it on parameter declarations, not on general things-of-function-type. This meant that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd), but required @noescape being allowed on arbitrary function types. Now that we have that, these two declarations are equivalent:

  func f(@noescape fn : () -> ()) {} // declaration attribute
  func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less pressing) has the exact same problem. That said, it is currently in a worse place than @noescape because you cannot actually spell the type of a function that involves it. Consider an autoclosure-taking function like this:

  func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

  f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

  let x = f2
  x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can see this if you force a type error:

  let y : Int = x // error: cannot convert value of type '(@autoclosure () -> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

  let x2 : (@autoclosure () -> ()) -> () = f2
  // error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the language, and seems silly that you can use type inference but not manual specification for the declaration of x2.

Proposed solution

The solution solution is straight-forward: disallow @noescape and @autoclosure on declarations, and instead require them on the types. This means that only the type-attribute syntax is supported:

  func f(fn : @noescape () -> ()) {} // type attribute.
  func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is “(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure () -> ()) -> ()”. This fixes the problem with x2, and eliminates the redundancy between the @noescape forms.

Impact on existing code

This breaks existing code that uses these in the old position, so it would be great to roll this out with the other disruptive changes happening in Swift 3. The Swift 3 migrator should move these over, and has the information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source incompatible with Swift 3 code in general, it seems best to make these a hard error in the final Swift 3 release. It would make sense to have a deprecation warning period for swift.org <http://swift.org/> projects like corelibs and swiftpm, and other open source users tracking the public releases though.

-Chris

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


#4

After this proposal [SE-0049] is accepted:

Is the following code valid?

func foo(arg: @autoclosure () -> Bool) { }
let value: @autoclosure () -> Bool = false
foo(value)

If so, I feel `@autoclosure T` is more natural than `@autoclosure () -> T`

let value: @autoclosure Bool = false
func foo(arg: @autoclosure Bool) {
   if arg() { ... }
}
foo(x)

···

2016-03-10 14:38 GMT+09:00 Chris Lattner via swift-evolution < swift-evolution@swift.org>:

*Introduction*
This proposal suggests moving the existing @noescape and @autoclosure
attributes from being declaration attributes on a parameter to being type
attributes. This improves consistency and reduces redundancy within the
language, e.g. aligning with the previous Swift 3 decision to move “inout”,
and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

*Motivation*
Chris Eidhof noticed an emergent result of removing our currying syntax:
it broke some useful code using @noescape, because we only allowed it on
parameter declarations, not on general things-of-function-type. This meant
that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (
https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd),
but required @noescape being allowed on arbitrary function types. Now that
we have that, these two declarations are equivalent:

func f(@noescape fn : () -> ()) {} // declaration attribute
func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less
pressing) has the exact same problem. That said, it is currently in a
worse place than @noescape because you cannot actually spell the type of a
function that involves it. Consider an autoclosure-taking function like
this:

func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

let x = f2
x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can
see this if you force a type error:

let y : Int = x // error: cannot convert value of type '(@autoclosure ()
-> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

let x2 : (@autoclosure () -> ()) -> () = f2
// error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the
language, and seems silly that you can use type inference but not manual
specification for the declaration of x2.

*Proposed solution*
The solution solution is straight-forward: disallow @noescape and
@autoclosure on declarations, and instead require them on the types. This
means that only the type-attribute syntax is supported:

func f(fn : @noescape () -> ()) {} // type attribute.
func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is
“(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure
() -> ()) -> ()”. This fixes the problem with x2, and eliminates the
redundancy between the @noescape forms.

*Impact on existing code*
This breaks existing code that uses these in the old position, so it would
be great to roll this out with the other disruptive changes happening in
Swift 3. The Swift 3 migrator should move these over, and has the
information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source
incompatible with Swift 3 code in general, it seems best to make these a
hard error in the final Swift 3 release. It would make sense to have a
deprecation warning period for swift.org projects like corelibs and
swiftpm, and other open source users tracking the public releases though.

-Chris

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


(Chris Lattner) #5

After this proposal [SE-0049] is accepted:

Is the following code valid?

func foo(arg: @autoclosure () -> Bool) { }
let value: @autoclosure () -> Bool = false
foo(value)

No, autoclosures may still only be used as parameters to functions.

-Chris

···

On Mar 27, 2016, at 10:24 PM, rintaro ishizaki <fs.output@gmail.com> wrote:

If so, I feel `@autoclosure T` is more natural than `@autoclosure () -> T`

let value: @autoclosure Bool = false
func foo(arg: @autoclosure Bool) {
   if arg() { ... }
}
foo(x)

2016-03-10 14:38 GMT+09:00 Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
Introduction

This proposal suggests moving the existing @noescape and @autoclosure attributes from being declaration attributes on a parameter to being type attributes. This improves consistency and reduces redundancy within the language, e.g. aligning with the previous Swift 3 decision to move “inout”, and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

Motivation

Chris Eidhof noticed an emergent result of removing our currying syntax: it broke some useful code using @noescape, because we only allowed it on parameter declarations, not on general things-of-function-type. This meant that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd), but required @noescape being allowed on arbitrary function types. Now that we have that, these two declarations are equivalent:

  func f(@noescape fn : () -> ()) {} // declaration attribute
  func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less pressing) has the exact same problem. That said, it is currently in a worse place than @noescape because you cannot actually spell the type of a function that involves it. Consider an autoclosure-taking function like this:

  func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

  f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

  let x = f2
  x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can see this if you force a type error:

  let y : Int = x // error: cannot convert value of type '(@autoclosure () -> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

  let x2 : (@autoclosure () -> ()) -> () = f2
  // error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the language, and seems silly that you can use type inference but not manual specification for the declaration of x2.

Proposed solution

The solution solution is straight-forward: disallow @noescape and @autoclosure on declarations, and instead require them on the types. This means that only the type-attribute syntax is supported:

  func f(fn : @noescape () -> ()) {} // type attribute.
  func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is “(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure () -> ()) -> ()”. This fixes the problem with x2, and eliminates the redundancy between the @noescape forms.

Impact on existing code

This breaks existing code that uses these in the old position, so it would be great to roll this out with the other disruptive changes happening in Swift 3. The Swift 3 migrator should move these over, and has the information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source incompatible with Swift 3 code in general, it seems best to make these a hard error in the final Swift 3 release. It would make sense to have a deprecation warning period for swift.org <http://swift.org/> projects like corelibs and swiftpm, and other open source users tracking the public releases though.

-Chris

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


(Howard Lovatt) #6

+1

···

On Monday, 28 March 2016, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

On Mar 27, 2016, at 10:24 PM, rintaro ishizaki <fs.output@gmail.com > <javascript:_e(%7B%7D,'cvml','fs.output@gmail.com');>> wrote:

After this proposal [SE-0049] is accepted:

Is the following code valid?

func foo(arg: @autoclosure () -> Bool) { }
let value: @autoclosure () -> Bool = false
foo(value)

No, autoclosures may still only be used as parameters to functions.

-Chris

If so, I feel `@autoclosure T` is more natural than `@autoclosure () -> T`

let value: @autoclosure Bool = false
func foo(arg: @autoclosure Bool) {
   if arg() { ... }
}
foo(x)

2016-03-10 14:38 GMT+09:00 Chris Lattner via swift-evolution <
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>>:

*Introduction*
This proposal suggests moving the existing @noescape and @autoclosure
attributes from being declaration attributes on a parameter to being type
attributes. This improves consistency and reduces redundancy within the
language, e.g. aligning with the previous Swift 3 decision to move “inout”,
and making declaration and type syntax more consistent.

Swift-evolution thread: <you are here>

*Motivation*
Chris Eidhof noticed an emergent result of removing our currying syntax:
it broke some useful code using @noescape, because we only allowed it on
parameter declarations, not on general things-of-function-type. This meant
that manually curried code like this:

func curriedFlatMap<A, B>(x: [A]) -> (@noescape A -> [B]) -> [B] {
   return { f in
       x.flatMap(f)
   }
}

Was rejected. Fixing this was straight-forward (
https://github.com/apple/swift/commit/c3c6beac72bc0368030f06d52c46b6444fc48dbd),
but required @noescape being allowed on arbitrary function types. Now that
we have that, these two declarations are equivalent:

func f(@noescape fn : () -> ()) {} // declaration attribute
func f(fn : @noescape () -> ()) {} // type attribute.

Further evaluation of the situation found that @autoclosure (while less
pressing) has the exact same problem. That said, it is currently in a
worse place than @noescape because you cannot actually spell the type of a
function that involves it. Consider an autoclosure-taking function like
this:

func f2(@autoclosure a : () -> ()) {}

You can use it as you’d expect, e.g.:

f2(print("hello”))

Of course, f2 is a first class value, so you can assign it:

let x = f2
x(print("hello"))

This works, because x has type "(@autoclosure () -> ()) -> ()”. You can
see this if you force a type error:

let y : Int = x // error: cannot convert value of type '(@autoclosure ()
-> ()) -> ()' to specified type 'Int'

However, you can’t write this out explicitly:

let x2 : (@autoclosure () -> ()) -> () = f2
// error: attribute can only be applied to declarations, not types

This is unfortunate because it is an arbitrary inconsistency in the
language, and seems silly that you can use type inference but not manual
specification for the declaration of x2.

*Proposed solution*
The solution solution is straight-forward: disallow @noescape and
@autoclosure on declarations, and instead require them on the types. This
means that only the type-attribute syntax is supported:

func f(fn : @noescape () -> ()) {} // type attribute.
func f2(a : @autoclosure () -> ()) {} // type attribute.

This aligns with the syntax used for types, since the type of “f” is
“(_: @noescape () -> ()) -> ()”, and the type of “f2” is “(_ : @autoclosure
() -> ()) -> ()”. This fixes the problem with x2, and eliminates the
redundancy between the @noescape forms.

*Impact on existing code*
This breaks existing code that uses these in the old position, so it
would be great to roll this out with the other disruptive changes happening
in Swift 3. The Swift 3 migrator should move these over, and has the
information it needs to do a perfect migration in this case.

For the compiler behavior, given that Swift 2.2 code will be source
incompatible with Swift 3 code in general, it seems best to make these a
hard error in the final Swift 3 release. It would make sense to have a
deprecation warning period for swift.org projects like corelibs and
swiftpm, and other open source users tracking the public releases though.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-- Howard.