profile
viewpoint

apple/swift-format 1119

Formatting technology for Swift source code

tkremenek/swift 5

The Swift Programming Language

tkremenek/swift-corelibs-foundation 1

The Foundation Project, providing core utilities, internationalization, and OS independence

tkremenek/swift-package-manager 1

The Package Manager for the Swift Programming Language

tkremenek/rouge 0

A pure Ruby code highlighter that is compatible with Pygments

tkremenek/swift-corelibs-xctest 0

The XCTest Project, A Swift core library for providing unit test support

tkremenek/swift-lldb 0

This is the version of LLDB that supports the Swift programming language & REPL.

tkremenek/swift-source-compat-suite 0

The infrastructure and project index comprising the Swift source compatibility suite.

pull request commentapple/swift-corelibs-libdispatch

docs: fix simple typo, transiton -> transition

@swift-ci please test

timgates42

comment created time in an hour

CommitCommentEvent

Pull request review commentapple/swift-evolution

[Proposal Revision] Extend property wrappers to functions and closure parameters.

 func buy(  ## Detailed design -Property wrappers are essentially sugar wrapping a given declaration with compiler-synthesized code. This proposal retains this principle. Annotating a parameter declaration with a property-wrapper attribute allows the call-site to pass a wrapped value or a projected value, and the compiler will automatically initialize the backing wrapper to pass to the function. The function author can also use the property-wrapper syntax for accessing the backing wrapper and the projected value in the body of the function.+Property wrappers are essentially sugar wrapping a given declaration with compiler-synthesized code. Retaining this principle, a function can now be called with the wrapped and projected values. Namely, annotating a parameter declaration with a property-wrapper attribute changes the declaration’s type to the backing storage, and prompts the compiler to synthesize the wrapped and projected values. When the function is called the compiler will, also, insert a call to the appropriate property-wrapper initializer.

Ok, I see your point. Upon rereading the paragraph, I realize that expecting to hear about the current design (since it's in the section's name), I read about existing behavior, which is then paralleled to new behavior. I find this problematic because the reader will –– up to this point –– be unfamiliar with this new behavior. Thus, what do you think of simply moving the first two periods in the end?


Annotating a parameter declaration with a property-wrapper attribute allows the call-site to pass a wrapped value or a projected value, and the compiler will automatically initialize the backing wrapper to be passed to the function. The function author can also use the property-wrapper syntax for accessing the backing wrapper and the projected value within the body of the function. This behavior merely builds on property wrappers’ being sugar wrapping a given declaration with compiler-synthesized code.

filip-sakel

comment created time in an hour

CommitCommentEvent

Pull request review commentapple/swift-evolution

[Proposal Revision] Extend property wrappers to functions and closure parameters.

 func buy(  ## Detailed design -Property wrappers are essentially sugar wrapping a given declaration with compiler-synthesized code. This proposal retains this principle. Annotating a parameter declaration with a property-wrapper attribute allows the call-site to pass a wrapped value or a projected value, and the compiler will automatically initialize the backing wrapper to pass to the function. The function author can also use the property-wrapper syntax for accessing the backing wrapper and the projected value in the body of the function.+Property wrappers are essentially sugar wrapping a given declaration with compiler-synthesized code. Retaining this principle, a function can now be called with the wrapped and projected values. Namely, annotating a parameter declaration with a property-wrapper attribute changes the declaration’s type to the backing storage, and prompts the compiler to synthesize the wrapped and projected values. When the function is called the compiler will, also, insert a call to the appropriate property-wrapper initializer.

I think these changes have obscured the meaning/purpose of this paragraph.

Retaining this principle, a function can now be called with the wrapped and projected values

I don't think this makes sense without the context of "Annotating a parameter declaration with a property-wrapper attribute". Also, the fact that a function can be called with a wrapped value or a projected value of a property wrapper doesn't really follow from "retaining this principle"

annotating a parameter declaration with a property-wrapper attribute changes the declaration’s type to the backing storage

This isn't something we want to state unless we're explicitly talking about how the code is compiled. As far as the user is concerned, the function type does not take in the backing storage. That's why had worded the sentence to be about the call-site.

and prompts the compiler to synthesize the wrapped and projected values

The wrapped and projected values are not synthesized, they're provided by the user.

I'm not sure why this last sentence was removed:

The function author can also use the property-wrapper syntax for accessing the backing wrapper and the projected value in the body of the function

This is showcasing how the feature is useful for function authors because they now have property-wrapper syntax in the body of the function.

filip-sakel

comment created time in 3 hours

push eventapple/swift-evolution

Joe Groff

commit sha 753e81fad04f070a39df867f10ecab90fab84d49

begin review for SE-299

view details

push time in 3 hours

Pull request review commentapple/swift-evolution

[Proposal Revision] Extend property wrappers to functions and closure parameters.

 Property Wrappers were [introduced in Swift 5.1](https://github.com/apple/swift-  Property wrappers have undoubtably been very successful. Applying a property wrapper to a property is enabled by an incredibly lightweight and expressive syntax. For instance, frameworks such as [SwiftUI](https://developer.apple.com/documentation/swiftui/) and [Combine](https://developer.apple.com/documentation/combine) introduce property wrappers such as [`State`](https://developer.apple.com/documentation/swiftui/state), [`Binding`](https://developer.apple.com/documentation/swiftui/binding) and [`Published`](https://developer.apple.com/documentation/combine/published) to expose elaborate behavior through a succinct interface, helping craft expressive yet simple APIs. However, property wrappers are only applicable to local variables and type properties, shattering the illusion that they helped realize in the first place when working with parameters. -### Memberwise initialization+Property wrappers attached to parameters have a wide variety of use cases. We present a few examples here. -Currently, property-wrapper attributes on struct properties interact with function parameters through the struct's synthesized memberwise initializer. However, property-wrapper attributes are _not_ supported on function parameters. This leads to complicated and nuanced rules for which type, between the wrapped-value type and the backing property-wrapper type, the memberwise initializer accepts.+### Argument validation -The compiler will choose the wrapped-value type to offer a convenience to the call-site when the property wrapper has an initializer of the form `init(wrappedValue:)` accepting the wrapped-value type, as seen here:+Both library developers and language users often need to assert their assumptions, for which `precondition(_:_:)` is often used:  ```swift-import SwiftUI---struct TextEditor {--  @State var document: Optional<URL>-  +enum Product {+  case plainSandwich +  case grilledCheeseSandwich +  case avocadoToast } --func openEditor(with swiftFile: URL) -> TextEditor {-  TextEditor(document: swiftFile) -  // The wrapped type is accepted here.+func buy(quantity: Int, of product: Product) {+  precondition(quanity >= 1, "Invalid product quanity.")+  +  if quantity == 1 {+    ...+  } } ``` -However, this can take flexibility away from the call-site if the property wrapper has other `init` overloads, because the call-site _cannot_ choose a different initializer. Further, if the property wrapper is explicitly initialized via `init()`, then the memberwise initializer will choose the backing-wrapper type, even if the wrapper supports `init(wrappedValue:)`. This results in unnecessary boilerplate at call-sites that _do_ want to use `init(wrappedValue:)`:+The above code is quite clear; it has, though, the obvious drawback that changing the the condition to be asserted or its error message requires significant effort as a precondition statement is individually written for each function and manually documented.++Furthermore, supposing the above is library code, we may want to test for our precondition while offering an easy-to-debug way. So, using `Validation` from [`PropertyKit`](https://github.com/SvenTiigi/ValidatedPropertyKit) we can write:  ```swift-import SwiftUI+@propertyWrapper+struct Asserted<Value> {+  // The assertion will appear at the right file and line+  init(+    wrappedValue: Value, +    validation: Validation<Value>,+    file: StaticString = #file,+    line: UInt = #line+  ) {+    ...+  } +  var wrappedValue: Value { ... } -struct TextEditor {+  var projectedValue: Result<Value, ValidationResult> { ... }+} -  @State() var document: Optional<URL>++func buy(+  quantity: Int, +  of product: Product,+  file: StaticString = #file,+  line: UInt = #line+  // These are a lot of properties for every time+  // we want to check for the right quantity.+) {+  var validatedQuantity = Asserted(+    quantity,+    .greaterOrEqual(1),+    file: file, +    line: line+  )   +  if validatedQuantity.wrappedValue == 1 {+    // The lack of property-wrapper tranformation is+    // evident here.+    ...+  } }+```++This not only makes writing easy-to-maintain validations easy, but improves debugging for the the API's users as well. Unfortunately, it still lacks the elegant syntax property wrappers offer, making the creation of new functions a demanding task.++### Pass-by-value for reference types +The `@NSCopying` attribute is a tool to emulate value semantics for reference-type properties. The same functionality can now be implemented as a property wrapper, as shown in [SE-0258](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#nscopying): -func openEditor(with swiftFile: URL) -> TextEditor {-  TextEditor(document: State(wrappedValue: swiftFile))-  // The wrapped type isn't accepted here; instead we have -  // to use the backing property-wrapper type: 'State'.+```swift+@propertyWrapper+struct Copying<Value: NSCopying> {+  private var _value: Value++  init(wrappedValue value: Value) {+    // Copy the value on initialization.+    self._value = value.copy() as! Value+  }++  var wrappedValue: Value {+    get { return _value }+    set {+      // Copy the value on reassignment.+      _value = newValue.copy() as! Value+    }+  } } ``` -Note also that the argument label does not change when the memberwise initializer uses the backing wrapper type instead of the wrapped-value type.+However, this property wrapper cannot be used on parameters to achieve pass-by-value semantics for reference-type arguments. To achieve pass-by-value semantics, `copy()` must be called manually, which is easy to forget, or the `Copying` type must be used directly in an API, which causes each call-site to manually create an instance of `Copying`. -If the generated memberwise initializer always accepted the backing wrapper type while still allowing the call-site the convenience of automatically initializing the backing wrapper via a wrapped-value type, the mental model for property wrapper initialization would be greatly simplified. Moreover, this would provide more control over the backing-wrapper initialization at the call-site.--### Function parameters with property wrapper type+### Memberwise initialization -Using property-wrapper types for function parameters also results in boilerplate code, both in the function body and at the call-site:+Consider the following property wrapper, inspired by `@Traceable` from [David Piper's blog post](https://medium.com/better-programming/creating-a-history-with-property-wrappers-in-swift-5-1-4c0202060a7f), which tracks the history of a value:  ```swift+struct History<Value> { ... }+ @propertyWrapper-struct Lowercased {+struct Traceable<Value> { -  init(wrappedValue: String) { ... }+  init(wrappedValue value: Value) { ... } +  init(projectedValue: History<Value>) { ... } -  var wrappedValue: String {-    get { ... }-    set { ... }+  var wrappedValue: Value {+    get {+      return history.currentValue+    }+    set {+      history.append(newValue)+    }   }-  ++  var projectedValue: History<Value> { return history }++  private var history: History<Value>+ }+``` +This property wrapper can be initialized with a value to be traced, or with an existing history of a value being traced. Now consider the following model for a simple text editor that supports change tracking: -func postUrl(urlString: Lowercased) {-  guard let url = URL(string: urlString.wrappedValue) else { return }-    //                                 ^~~~~~~~~~~~~-    // We must access 'wrappedValue' manually.-  ...+```swift+struct TextEditor {+  @Traceable var dataSource: String }+```++Currently, property-wrapper attributes on struct properties interact with function parameters through the struct's synthesized memberwise initializer. Because the `@Traceable` property wrapper supports initialization from a wrapped value via `init(wrappedValue:)`, the memberwise initializer for `TextEditor` will take in a `String`. However, the programmer may want to initialize `TextEditor` with a string value that already has a history. Today, this can be achieved with overloads, which can greatly impact compile-time performance, or by exposing the `Traceable` type through the `TextEditor` initializer, which is meant to be implementation detail.++## Proposed solution +We propose to allow application of property wrappers on function and closure parameters, allowing the call-site to pass a wrapped value or a projected value which will be used to automatically initialize the backing property wrapper. -postUrl(urlString: Lowercased(wrappedValue: "mySite.xyz/myUnformattedUsErNAme"))-//                 ^~~~~~~~~~-// We must initialize `Lowercased` manually,-// instead of automatically initializing-// from its wrapped value type.+Using property-wrapper parameters, the above argument validation example can be simplified to:++```swift+func buy(+  @Asserted(.greaterOrEqual(1)) quantity: Int,+  of product: Product,+) {+  if quantity == 1 {+    ...+  }+} ``` -In the above example, the inability to apply property wrappers to function parameters prevents the programmer from removing unnecessary details from the code. The call-site of `postUrl` is forced to initialize an instance of `Lowercased` manually using `init(wrappedValue:)`, even though this initailization is automatic when using `@Lowercased` on a local variable or type property. Further, manually accessing `wrappedValue` in the function body can be distracting when trying to understand the implementation. These limitations are emphasized by the fact that property wrappers were originally sought out to eliminate such boilerplate.+## Detailed design++Property wrappers are essentially sugar wrapping a given declaration with compiler-synthesized code. Retaining this principle, a function can now be called with the wrapped and projected values. Namely, annotating a parameter declaration with a property-wrapper attribute changes the declaration’s type to the backing storage, and prompts the compiler to synthesize the wrapped and projected values. Furthermore, when the function is called the compiler will insert a call to the appropriate property-wrapper initializer. -### Closures accepting property-wrapper types+### Function-body semantics -Consider the following SwiftUI code, which uses [`ForEach`](https://developer.apple.com/documentation/swiftui/foreach) over a collection:+Attaching a property wrapper to a parameter makes that parameter a computed variable local to the function body, and changes the parameter type to the backing wrapper type. The type of the parameter is only observable in compiled code - [unapplied references to functions with property-wrapped parameters](#unapplied-function-references) will not use the backing-wrapper type.++The transformation of function with a property-wrapped parameter will be performed as such:++1. The argument label will remain unchanged. +2. The parameter name will be prefixed with an underscore.+3. The type of the parameter will be the backing property-wrapper type.+4. A local computed property representing the `wrappedValue` of the innermost property wrapper will be synthesized with the same name as the original, unprefixed parameter name. If the innermost `wrappedValue` defines a setter, a setter will be synthesized for the local property if the mutability of the composed setter is `nonmutating`. The mutability computation is specified in the [appendix](#appendix).+5. If the outermost property wrapper defines a `projectedValue` property with a `nonmutating` getter, a local computed property representing the outermost `projectedValue` will be synthesized and named per the original parameter name prefixed with a dollar sign (`$`). If the outermost `projectedValue` defines a setter, a setter for the local computed property will be synthesized if the `projectedValue` setter is `nonmutating`.++Consider the following function with a property-wrapped parameter using the `@Asserted` property wrapper:  ```swift-struct MyView : View {+func insert(@Asserted(.nonEmpty) text: String) { ... }+``` -  // A simple Shopping Item that includes-  // a 'quantity' and a 'name' property.-  @State-  private var shoppingItems: [Item]+The compiler will synthesize computed `text` and `$text` variables in the body of `insert`: -  var body: some View {-    ForEach(0 ..< shoppingItems.count) { index in-      TextField(shoppingItems[index].name, $shoppingItems[index].name)-    }+```swift+func insert(text _text: Asserted<String>) {+  var text: String {+    get { _text.wrappedValue }+  }++  var $text: Result<String, ValidationResult> {+    get { _text.projectedValue }   } +  ... } ``` -Working with `shoppingItems` in the closure body is painful, because the code must manually index into the original wrapped property, rather than working with collection elements directly in the closure. The manual indexing would be alleviated if the closure accepted `Binding`s to collection elements:+### Call-site semantics++When passing an argument to a function with a property-wrapped parameter, the compiler will wrap the argument in a call to the appropriate initializer depending on the argument label. When using the original argument label (or no argument label), the compiler will wrap the argument in a call to `init(wrappedValue:)`. When using the argument label prefixed with `$` (or `$_` in the case of no argument label), the compiler will wrap the argument in a call to `init(projectedValue:)`. ++Consider the `@Traceable` property wrapper that implements both `init(wrappedValue:)` and `init(projectedValue:)`:  ```swift-struct MyView : View {+struct History<Value> { ... } -  // A simple Shopping Item that includes-  // a 'quantity' and a 'name' property.-  @State-  private var shoppingItems: [Item]+@propertyWrapper+struct Traceable<Value> { -  var body: some View {-    ForEach($shoppingItems) { itemBinding in-      TextField(itemBinding.wrappedValue.name, itemBinding.name)-    }-  }+  init(wrappedValue value: Value)++  init(projectedValue: History<Value>)++  var wrappedValue: Value++  var projectedValue: History<Value>  } ``` -However, now we observe the same boilerplate code in the closure body because the property-wrapper syntax cannot be used with the closure parameter.+A function with an `@Traceable` parameter can be called with either a wrapped value or a projected value: -## Proposed solution+```swift+func log<Value>(@Traceable value: Value) { ... } -We propose to allow application of property wrappers on function and closure parameters.+let history: History<Int> = ...+log(value: 10)+log($value: history)+``` -Using property-wrapper parameters, the above `postUrl` example becomes:+The compiler will inject a call to the appropriate property-wrapper initializer into each call to `log` based on the argument label, so the above code is transformed to:  ```swift-func postUrl(@Lowercased urlString: String) {-  guard let url = URL(string: urlString) else { return }-  ...-}--postUrl(urlString: "mySite.xyz/myUnformattedUsErNAme")+log(value: Traceable(wrappedValue: 10))+log(value: Traceable(projectedValue: history)) ``` -In the above SwiftUI example, if collection elements could be accessed via `Binding`s in the `ForEach` closure, property-wrapper parameters could be used to enable property-wrapper syntax in the closure body:+Wrapped parameters with no argument label can still be passed a projection using the syntax `$_:`, as shown in the following example:  ```swift-struct MyView: View {+func log<Value>(@Traceable _ value: Value) { ... }++let history: History<Int> = ...+log(10)+log(_: 10)+log($_: history)+``` -  @State-  private var shoppingItems: [Item]+For composed property wrappers, initialization of the backing wrapper via wrapped value will contain a call to `init(wrappedValue:)` for each property-wrapper attribute in the composition chain. However, initialization via projected value will only contain one call to `init(projectedValue:)` for the outermost wrapper attribute, because property wrapper projections are not composed. For example:  -  var body: some View {-    ForEach($shoppingItems) { (@Binding item) in-      TextField(item.name, $item.name)-    }-  }+```swift+func log(@Traceable @Traceable text: String) { ... } -}+let history: History<Traceable<String>> = ...+log(text: "Hello!")+log($text: history) ``` -## Detailed design+The above calls to `log` are transformed to: -Property wrappers are essentially sugar wrapping a given property with compiler-synthesized code. This proposal retains this principle, employing the following transformation.+```swift+log(text: Traceable(wrappedValue: Traceable(wrappedValue: "Hello!"))+log(text: Traceable(projectedValue: history))+``` -### Function body transformation+This transformation at the call-site only applies when calling the function directly using the declaration name. The semantics of closures and unapplied function references are specified [in a later section](#semantics-of-function-expressions). -The transformation of function with a property-wrapper parameter will be performed as such:+#### Passing a projected value argument -1. For regular functions, the argument label will remain unchanged. -2. The parameter name will be prefixed with an underscore.-3. The type of the parameter will be the backing property-wrapper type.-4. A local computed property representing the `wrappedValue` of the innermost property wrapper will be synthesized with the same name as the original, unprefixed parameter name. If the innermost `wrappedValue` defines a setter, a setter will be synthesized for the local property if the mutability of the composed setter is `nonmutating`. The mutability computation is specified below.-5. If the outermost property wrapper defines a `projectedValue` property, a local computed property representing the outermost `projectedValue` will be synthesized and named per the original parameter name prefixed with a dollar sign (`$`). If the outermost `projectedValue` defines a setter, a setter for the local computed property will be synthesized if the `projectedValue` setter is `nonmutating`, or if the outermost wrapper is a reference type.+Property wrappers can opt into passing a projected-value argument to a property-wrapped parameter. -#### Mutability of composed `wrappedValue` accessors+Though property-wrapper projections can be utilized to expose arbitrary API through the synthesized `$` property, projections are typically used to either publicly expose the backing property wrapper directly, or to provide a public representation of the backing wrapper that's suitable for use outside of the declaration that owns the wrapper storage. In such cases, supporting property-wrapper initialization from a projected-value is very useful, especially if the wrapper does not support `init(wrappedValue:)`. To support passing a property-wrapper projection to a function with a wrapped parameter, property wrappers can implement `init(projectedValue:)`. -The computation for mutability of a wrapped parameter's composed `wrappedValue` accessors will be the same as it is today for wrapped properties. The computation starts with the mutability of the outermost wrapper's `wrappedValue` accessor, and then iterates over the chain of composed property wrappers, "composing" the mutability of each `wrappedValue` accessor along the way using the following rules, which are the same for getters and setters:+Presence of an `init(projectedValue:)` that meets the following requirements enables passing a projected value via the `$` calling syntax: -* If the next `wrappedValue` accessor is `nonmutating`, then the mutability of the composed accessor is the same as the previous composed getter. If the wrapper is a reference type, the accessor is considered `nonmutating`.-* If the next `wrappedValue` accessor is `mutating`, then the composed accessor is `mutating` if the previous composed getter _or_ setter is `mutating`, since both are needed to perform a writeback cycle.+- The first parameter of this initializer must be labeled `projectedValue` and have the same type as the `var projectedValue` property.

If I understand correctly, declaring a projectedValue property is not mandatory; should we perhaps state this explicitly?

We should probably add a rationale for this behavior, as well.

filip-sakel

comment created time in 5 hours

Pull request review commentapple/swift-syntax

Make SourceLocation and related types Hashable

 struct ComputedLocation: Codable, CustomDebugStringConvertible { }  /// Represents a source location in a Swift file.-public struct SourceLocation: Codable, CustomDebugStringConvertible {+public struct SourceLocation: Hashable, Codable, CustomDebugStringConvertible {

That’s a good point. I didn’t consider file. With that in mind, I

MaxDesiatov

comment created time in 11 hours

created tagapple/swift-tools-support-core

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

Contains common infrastructural code for both SwiftPM and llbuild.

created time in 11 hours

created tagapple/swift-syntax

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

SwiftPM package for SwiftSyntax library.

created time in 11 hours

created tagapple/swift-xcode-playground-support

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

Logging and communication to allow Swift toolchains to communicate with Xcode.

created time in 11 hours

created tagapple/swift-integration-tests

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

Automated tests for validating the generated Swift snapshots behave correctly

created time in 11 hours

created tagapple/swift-corelibs-libdispatch

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

The libdispatch Project, (a.k.a. Grand Central Dispatch), for concurrency on multicore hardware

created time in 11 hours

created tagapple/swift-corelibs-xctest

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-17-a

The XCTest Project, A Swift core library for providing unit test support

created time in 11 hours

Pull request review commentapple/swift-syntax

Make SourceLocation and related types Hashable

 struct ComputedLocation: Codable, CustomDebugStringConvertible { }  /// Represents a source location in a Swift file.-public struct SourceLocation: Codable, CustomDebugStringConvertible {+public struct SourceLocation: Hashable, Codable, CustomDebugStringConvertible {

In my opinion they shouldn't be equal since line, column, and file will return different results for them. The one without a ComputedLocation could refer to a different file altogether, as file is inferred from compLoc.

MaxDesiatov

comment created time in 11 hours

Pull request review commentapple/swift-syntax

Make SourceLocation and related types Hashable

 struct ComputedLocation: Codable, CustomDebugStringConvertible { }  /// Represents a source location in a Swift file.-public struct SourceLocation: Codable, CustomDebugStringConvertible {+public struct SourceLocation: Hashable, Codable, CustomDebugStringConvertible {

Just to be clear: Should two SourceLocations be different if one has a ComputedLocation and the other does not? After all, they still refer to the same location in the source code.

I’m not 100% sure on this, but I’m leaning towards making the identity of SourceLocation be purely based on the value of offset. What do you think, @MaxDesiatov?

CC: @akyrtzi

MaxDesiatov

comment created time in 13 hours

Pull request review commentapple/swift-evolution

[Proposal Revision] Extend property wrappers to functions and closure parameters.

 generic(arg: [1, 2, 3]) // calls the constrained init(wrappedValue:)                         // because the argument conforms to Collection. ``` -### Closures and unapplied function references+### Semantics of function expressions -By default, closures and unapplied references to functions that accept property-wrapped parameters use the wrapped-value type in the parameter list, and the compiler will generate a thunk to initialize the backing wrapper and call the function.+#### Unapplied function references -Consider the following function which uses the [`@Clamping`](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds) property wrapper:+By default, unapplied references to functions that accept property-wrapped parameters use the wrapped-value type in the parameter list, and the compiler will generate a thunk to initialize the backing wrapper and call the function.++Consider the `log` function from above, which uses the `@Traceable` property wrapper:  ```swift-func reportProgress(@Clamping(min: 0, max: 100) percent: Int) { ... }+func log<Value>(@Traceable value: Value) { ... } ``` -The type of `reportProgress` is `(Int) -> Void`. These semantics can be observed when working with an unapplied reference to `reportProgress`:+The type of `log` is `(Value) -> Void`. These semantics can be observed when working with an unapplied reference to `log`:  ```swift-let fnRef: (Int) -> Void = reportProgress+let fnRef: (Int) -> Void = log fnRef(10)++let fnRefWithLabel: (Int) -> Void = log(value:)+fnRefWithLabel(10) ``` -The compiler will generate a thunk when referencing `reportProgress` to take in the wrapped-value type and initialize the backing property wrapper:+The compiler will generate a thunk when referencing `log` to take in the wrapped-value type and initialize the backing property wrapper. Both references to `log` in the above example are transformed to:  ```swift-let fnRef: (Int) -> Void =  { reportProgress(percent: Clamping(wrappedValue: $0, min: 0, max: 100) }+{ log(value: Traceable(wrappedValue: $0) } ``` -The type of a closure or unapplied function reference can be changed to instead take in the projected-value type using `$` in front of the parameter name in a closure or in front of the argument label in a function reference. Consider the following `UnsafeMutableReference` property wrapper that projects an `UnsafeMutablePointer` and implements `init(projectedValue:)`:+The type of an unapplied function reference can be changed to instead take in the projected-value type using `$` in front of the argument label. Since `Traceable` implements `init(projectedValue:)`, the `log` function can be referenced in a way that takes in `History` by using `$` in front of `value`:  ```swift-@propertyWrapper-struct UnsafeMutableReference<Value> {+let history: History<Int> = ...+let fnRef: (History<Int>) -> Void = log($value:)+fnRef(history)+``` -  init(projectedValue: UnsafeMutablePointer<Value>) { ... }+If the property-wrapped parameter in `log` omits an argument label, the function can still be referenced to take in the projected-value type using `$_`: -}+```swift+func log<Value>(@Traceable _ value: Value) { ... }++let history: History<Int> = ...+let fnRef: (History<Int>) -> Void = log($_:)

Name suggestions: functionReference, logReference, reference. Having 'fnRef' seems a bit unswifty.

filip-sakel

comment created time in a day

pull request commentapple/swift-algorithms

Add `contains(countIn:where:)` to `Sequence`

It's also worth considering whether the performance gained is a constant factor (as here) or, say, takes something from O(n^2) to O(n).

Only if you compare it to the prefix version that I don't expect many people to come up with — something more straight-forward like .count > k can still be slower by more than a constant factor.

mdznr

comment created time in a day

push eventapple/swift-evolution

Max Desiatov

commit sha ea73f60935b3d0f0730920bcb386225fa6b6ec80

Enable Swift syntax highlighting in SE-0300 (#1252)

view details

push time in a day

pull request commentapple/swift-evolution

Enable Swift syntax highlighting in SE-0300

thx!

MaxDesiatov

comment created time in a day

PR closed apple/swift-algorithms

Add `lastIndex` variants returning `Range<Index>?`

Description

Closes #23

Detailed Design

extension BidirectionalCollection {
  public func lastIndexAsRange(
    where predicate: (Element) throws -> Bool
  ) rethrows -> Range<Index>?
}

extension BidirectionalCollection where Element: Equatable {
  public func lastIndexAsRange(
    of element: Element
  ) -> Range<Index>?
}

Documentation Plan

  • README.md (added link to guide).
  • Guides/LastIndex.md (added example code).
  • Sources/Algorithms/LastIndex.swift (added documentation comments).

Test Plan

  • Tests/SwiftAlgorithmsTests/LastIndexTests.swift

Source Impact

None (new API only).

Checklist

  • [x] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+126 -0

5 comments

4 changed files

benrimmington

pr closed time in a day

pull request commentapple/swift-algorithms

Add `lastIndex` variants returning `Range<Index>?`

Got it: I think I would agree with @natecook1000 that, if in the predominant use cases you tend to want just one index or the other, then the API would be less cumbersome returning the one thing you actually want, at the expense of requiring another operation in the rare case you want both.

benrimmington

comment created time in a day

pull request commentapple/swift-algorithms

Add `contains(countIn:where:)` to `Sequence`

@kylemacomber Thanks—that's informative.

These APIs are really meant to be composable, and I think we need to think hard about what APIs we offer strictly to squeeze out performance gains and how much that can justify the burden of adding, maintaining, and having users learn a new API.

I would propose that any APIs added for this purpose should themselves be deemed useful for further composition and, indeed, be more efficient building blocks than what we currently have. It's also worth considering whether the performance gained is a constant factor (as here) or, say, takes something from O(n^2) to O(n).

mdznr

comment created time in a day

pull request commentapple/swift-algorithms

Add `lastIndex` variants returning `Range<Index>?`

It seem kind of a niche operation: What's the use case?

benrimmington

comment created time in a day

pull request commentapple/swift-algorithms

Add `lastIndex` variants returning `Range<Index>?`

because they could be misinterpreted as returning a range containing (potentially) more than one index.

Wait...it doesn't do that? Well, now you have empiric evidence that this API has been misinterpreted with its current naming anyway.

It seem kind of a niche operation: What's the use case? Can we come up with something that can be re-used more widely which would serve the same use case?

benrimmington

comment created time in a day

created tagapple/swift-tools-support-core

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-16-a

Contains common infrastructural code for both SwiftPM and llbuild.

created time in a day

created tagapple/swift-syntax

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-16-a

SwiftPM package for SwiftSyntax library.

created time in a day

created tagapple/swift-xcode-playground-support

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-16-a

Logging and communication to allow Swift toolchains to communicate with Xcode.

created time in a day

created tagapple/swift-integration-tests

tagswift-DEVELOPMENT-SNAPSHOT-2021-01-16-a

Automated tests for validating the generated Swift snapshots behave correctly

created time in a day

more