It's the little things


(John Holdsworth) #1

As the festive season approaches I thought I’d write a brain dump
of a few things I'd hope Santa could bring in his bag for Swift 3.1.
No big ideas, just things that bug me day to day and would be
backward source compatible.

I’m sure it’s been discussed before but I wish static and class vars and
functions were in scope for instance methods without having to prefix them
as they are in Java. I’d go further and say they should be available when
referring to an object outside the class as if they were instance methods
i.e. object.classMethod().

Occasionally I find myself wishing I could give a static variable function
or method scope i.e. declare it inside the function or method if there are
no other references to localise access.

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

  let a = key != nil ? dict[key] : nil

you could just write:

  let a = dict[key?]

or even

  let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

As subscripts are functions these are probably the same thing.

One thing I encountered the recently writing a (bad) regular expression
library was that I wish getters were not required for subscript overloads.
In the library I wanted to write multiple setters for different types being
assigned but you only want one declared getter or you get ambiguities.

And of course, multi-line string literals using either “””long string””” or <<“HEREDOC”.

John


(David Sweeris) #2

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

   let a = key != nil ? dict[key] : nil

you could just write:

   let a = dict[key?]

or even

   let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

The first part is pretty easy to add in an extension:

extension Dictionary {
    subscript(_ key:Key?) -> Value? {
        return key != nil ? self[key!] : nil
    }
}

At least I think that works... I'm on my phone so I can't test it.

- Dave Sweeris

···

Sent from my iPhone

On Dec 12, 2016, at 16:15, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:


(Xiaodi Wu) #3

As the festive season approaches I thought I’d write a brain dump
of a few things I'd hope Santa could bring in his bag for Swift 3.1.
No big ideas, just things that bug me day to day and would be
backward source compatible.

I’m sure it’s been discussed before but I wish static and class vars and
functions were in scope for instance methods without having to prefix them
as they are in Java.

I agree that it's annoying to have to prefix them with
`VeryLongTypeNames.everyTime()`. Fortunately, SE-0068 was already approved
a long time ago that makes this a lot better, though it's not the specific
solution you're advocating for. Namely, we'll be able to write
`Self.everyTime()` instead, and IMO it can't come fast enough. It's just
that no one has stepped up to implement it. Santa?

I’d go further and say they should be available when
referring to an object outside the class as if they were instance methods
i.e. object.classMethod().

Occasionally I find myself wishing I could give a static variable function
or method scope i.e. declare it inside the function or method if there are
no other references to localise access.

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

        let a = key != nil ? dict[key] : nil

you could just write:

        let a = dict[key?]

or even

        let a = func( arg: argumentThatMayBeNull? ) // not called if
argument is nil

As subscripts are functions these are probably the same thing.

We've had several discussions about this particular proposal on the list,
as you've alluded to. Core team members have chimed in and said that this
was considered and rejected. One specific point has convinced me that it is
a superficially delightful idea with _very_ high footgun potential--

The expression `a()?.b()?.c()` clearly short-circuits. That is, if `a() ==
nil`, then `b()` is never called. It's not just a way of making code
terser; the short-circuiting behavior is critical.

Now, given `let result = foo(a(), b()?, c(), d()?)`, how does that line of
code short-circuit, or should all arguments be evaluated regardless?

There is no single obviously correct answer. Is a() called before b()
because it's the first argument--as it would be if we had written `foo(a(),
b(), c(), d())`--or is b() called before a() because it's the first
potentially short-circuiting argument, by analogy with `a()?.b()?.c()`? Is
c() always called, as it would be by analogy with `foo(a(), b(), c(),
d())`, or sometimes not called, by analogy with `a()?.b()?.c()`?

Now if we *do* short-circuit, suppose the signature of foo is changed in a
subsequent version of a library so that you must supply the arguments in a
different order, what then? Suppose we have now `let result = foo(d()?,
c(), b()?, a())` and that a(), b(), c(), d() have side effects. All of a
sudden, a seemingly innocuous change by a library vendor could totally
change the behavior of our code.

You might say: it's uncommon to nest functions like that. Maybe, but keep
in mind that computed properties and subscripts are just functions. It
cannot be known on inspection whether any particular property is computed
and might have side effects. This has convinced me that the potential for
this syntax to be silently tricky outweighs the benefits of shorter syntax,
and that the core team made the right choice in rejecting it initially.

Plus, that's what `flatMap` is for: `let a = key.flatMap { dict[$0] }`.

One thing I encountered the recently writing a (bad) regular expression

library was that I wish getters were not required for subscript overloads.
In the library I wanted to write multiple setters for different types being
assigned but you only want one declared getter or you get ambiguities.

And of course, multi-line string literals using either “””long string”””
or <<“HEREDOC”.

I'm sure that will be a lively discussion in Swift 4 phase 2 in the new
year. Happy holidays everyone :slight_smile:

···

On Mon, Dec 12, 2016 at 6:15 PM, John Holdsworth via swift-evolution < swift-evolution@swift.org> wrote:

John

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


(Chris Lattner) #4

You can do something like this, but I’d recommend labeling the subscript. The problem comes up when you have a dictionary that has an optional key: When you use “myDict[nil]”, you may get one or the other, but you probably mean one specifically.

Using a label on the subscript solves this, and makes the code more explicit that you’re not just getting the normal subscript that everyone would expect.

-Chris

···

On Dec 12, 2016, at 6:58 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 12, 2016, at 16:15, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

  let a = key != nil ? dict[key] : nil

you could just write:

  let a = dict[key?]

or even

  let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

The first part is pretty easy to add in an extension:

extension Dictionary {
   subscript(_ key:Key?) -> Value? {
       return key != nil ? self[key!] : nil
   }
}

At least I think that works... I'm on my phone so I can't test it.


(Joe Groff) #5

Allowing `?` at an arbitrary position like this has ambiguity problems, since it isn't clear how much of the enclosing expression needs to be conditionalized on the unwrapped optional. This would be hard for humans to read, and it'd be an exponential search for the compiler to find a solution that works. You can use Optional's `map` and `flatMap` to chain an arbitrary closure, though. `dict[key?]` would be `key.flatMap { dict[$0] }`, and `func(arg: optional?)` would be `optional.flatMap { func(arg: $0) }`.

-Joe

···

On Dec 12, 2016, at 4:15 PM, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:

As the festive season approaches I thought I’d write a brain dump
of a few things I'd hope Santa could bring in his bag for Swift 3.1.
No big ideas, just things that bug me day to day and would be
backward source compatible.

I’m sure it’s been discussed before but I wish static and class vars and
functions were in scope for instance methods without having to prefix them
as they are in Java. I’d go further and say they should be available when
referring to an object outside the class as if they were instance methods
i.e. object.classMethod().

Occasionally I find myself wishing I could give a static variable function
or method scope i.e. declare it inside the function or method if there are
no other references to localise access.

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

  let a = key != nil ? dict[key] : nil

you could just write:

  let a = dict[key?]

or even

  let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

As subscripts are functions these are probably the same thing.


(John Holdsworth) #6

I was going to have a look at creating a new proposal for this but I see there already
is one <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160425/015977.html> and the start of an implementation <https://github.com/apple/swift/pull/3866>. Is there any chance this is in the pipeline?

- John

···

On 13 Dec 2016, at 02:13, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Dec 12, 2016 at 6:15 PM, John Holdsworth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I’m sure it’s been discussed before but I wish static and class vars and
functions were in scope for instance methods without having to prefix them
as they are in Java.

I agree that it's annoying to have to prefix them with `VeryLongTypeNames.everyTime()`. Fortunately, SE-0068 was already approved a long time ago that makes this a lot better, though it's not the specific solution you're advocating for. Namely, we'll be able to write `Self.everyTime()` instead, and IMO it can't come fast enough. It's just that no one has stepped up to implement it. Santa?


(Callionica (Swift)) #7

If not using '?' at arbitrary positions, but instead applying '?' to the
function call, it looks like it's possible to enable OK syntax by defining
a custom operator (at least for unnamed args):
let result = fn<-?(a, b)
Here:
- fn is a function that has two non-optional parameters
- a and b are optionals of the same type as the parameters
- fn is called if both a and b are non-nil
- result is nil if either a or b are nil, otherwise it's the value
returned by calling fn

Noodles at:
https://gist.github.com/callionica/11cb880c1752b1098b012d67d693d9cc

Kind of feels like one of the functional operator libraries should already
have operators that can optionalize non-optional functions, so I'd look
there first.

-- Callionica

···

On Tue, Dec 13, 2016 at 10:37 AM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

> On Dec 12, 2016, at 4:15 PM, John Holdsworth via swift-evolution < > swift-evolution@swift.org> wrote:
>
> As the festive season approaches I thought I’d write a brain dump
> of a few things I'd hope Santa could bring in his bag for Swift 3.1.
> No big ideas, just things that bug me day to day and would be
> backward source compatible.
>
> I’m sure it’s been discussed before but I wish static and class vars and
> functions were in scope for instance methods without having to prefix
them
> as they are in Java. I’d go further and say they should be available when
> referring to an object outside the class as if they were instance methods
> i.e. object.classMethod().
>
> Occasionally I find myself wishing I could give a static variable
function
> or method scope i.e. declare it inside the function or method if there
are
> no other references to localise access.
>
> I’d like to raise again the idea of optionality when referencing a key or
> calling a function could be possible using a ? i.e instead of
>
> let a = key != nil ? dict[key] : nil
>
> you could just write:
>
> let a = dict[key?]
>
> or even
>
> let a = func( arg: argumentThatMayBeNull? ) // not called if
argument is nil
>
> As subscripts are functions these are probably the same thing.

Allowing `?` at an arbitrary position like this has ambiguity problems,
since it isn't clear how much of the enclosing expression needs to be
conditionalized on the unwrapped optional. This would be hard for humans to
read, and it'd be an exponential search for the compiler to find a solution
that works. You can use Optional's `map` and `flatMap` to chain an
arbitrary closure, though. `dict[key?]` would be `key.flatMap { dict[$0]
}`, and `func(arg: optional?)` would be `optional.flatMap { func(arg: $0)
}`.

-Joe

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


(David Sweeris) #8

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

let a = key != nil ? dict[key] : nil

you could just write:

let a = dict[key?]

or even

let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

The first part is pretty easy to add in an extension:

extension Dictionary {
  subscript(_ key:Key?) -> Value? {
      return key != nil ? self[key!] : nil
  }
}

At least I think that works... I'm on my phone so I can't test it.

You can do something like this, but I’d recommend labeling the subscript. The problem comes up when you have a dictionary that has an optional key: When you use “myDict[nil]”, you may get one or the other, but you probably mean one specifically.

I don’t think that’s an issue in the stdlib, because `Optional` doesn’t conform to `Hashable` and, AFAIK, no other stdlib types conform to `ExpressibleByNilLiteral`. Custom types could conform to both, though, and according to a playground, that does indeed lead to some confusing code:
struct Foo : ExpressibleByNilLiteral, Hashable {...}
extension Dictionary { subscript(_ key:Key?) -> Value? { return key != nil ? self[key!] : nil } }
var bar = [Foo:Int]()
bar[nil] //calls `Foo.init(nilLiteral:())`, and tries to look up the new `Foo` in `bar` using the stdlib's subscript
bar[nil as Foo?] //passes `Optional<Foo>.none, which uses the extension's subscript

Using a label on the subscript solves this, and makes the code more explicit that you’re not just getting the normal subscript that everyone would expect.

Yeah, that would certainly solve it. Kind of a shame, though, since it’d be one less function to think about, and 99.998% of the time it’d give the right answer. Too bad we can’t extend stuff where some condition isn’t met, like "extension Dictionary where !(Key: ExpressibleByNilLiteral) {…}” or something.

- Dave Sweeris

···

On Dec 13, 2016, at 9:51 AM, Chris Lattner <clattner@apple.com> wrote:
On Dec 12, 2016, at 6:58 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 12, 2016, at 16:15, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:


(Joe Groff) #9

We haven't had time to invest in implementing this yet. Since it's a purely additive feature, the acceptance of the proposal still stand, and we would accept a good implementation of it at any time. It does however require some relatively deep refactoring of the modeling of Self types across the AST and SIL layers, which is currently very superficial, in order to successfully implement, which is part of why we haven't gotten around to it yet.

-Joe

···

On Apr 20, 2017, at 8:13 AM, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:

I was going to have a look at creating a new proposal for this but I see there already
is one <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160425/015977.html> and the start of an implementation <https://github.com/apple/swift/pull/3866>. Is there any chance this is in the pipeline?


(Jordan Rose) #10

I wouldn't be surprised if there's a proposal to make Optional conditionally conform to Hashable if/when we get conditional conformances.

Jordan

···

On Dec 13, 2016, at 20:43, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 13, 2016, at 9:51 AM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

On Dec 12, 2016, at 6:58 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 12, 2016, at 16:15, John Holdsworth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

let a = key != nil ? dict[key] : nil

you could just write:

let a = dict[key?]

or even

let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

The first part is pretty easy to add in an extension:

extension Dictionary {
  subscript(_ key:Key?) -> Value? {
      return key != nil ? self[key!] : nil
  }
}

At least I think that works... I'm on my phone so I can't test it.

You can do something like this, but I’d recommend labeling the subscript. The problem comes up when you have a dictionary that has an optional key: When you use “myDict[nil]”, you may get one or the other, but you probably mean one specifically.

I don’t think that’s an issue in the stdlib, because `Optional` doesn’t conform to `Hashable` and, AFAIK, no other stdlib types conform to `ExpressibleByNilLiteral`. Custom types could conform to both, though, and according to a playground, that does indeed lead to some confusing code:
struct Foo : ExpressibleByNilLiteral, Hashable {...}
extension Dictionary { subscript(_ key:Key?) -> Value? { return key != nil ? self[key!] : nil } }
var bar = [Foo:Int]()
bar[nil] //calls `Foo.init(nilLiteral:())`, and tries to look up the new `Foo` in `bar` using the stdlib's subscript
bar[nil as Foo?] //passes `Optional<Foo>.none, which uses the extension's subscript


(David Sweeris) #11

Probably, yeah... I'll be curious as to how they propose we prevent collisions between Optional<T>.none.hashValue and any given T's hash value.

- Dave Sweeris

···

On Dec 14, 2016, at 09:54, Jordan Rose <jordan_rose@apple.com> wrote:

On Dec 13, 2016, at 20:43, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 13, 2016, at 9:51 AM, Chris Lattner <clattner@apple.com> wrote:

On Dec 12, 2016, at 6:58 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 12, 2016, at 16:15, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:

I’d like to raise again the idea of optionality when referencing a key or
calling a function could be possible using a ? i.e instead of

let a = key != nil ? dict[key] : nil

you could just write:

let a = dict[key?]

or even

let a = func( arg: argumentThatMayBeNull? ) // not called if argument is nil

The first part is pretty easy to add in an extension:

extension Dictionary {
  subscript(_ key:Key?) -> Value? {
      return key != nil ? self[key!] : nil
  }
}

At least I think that works... I'm on my phone so I can't test it.

You can do something like this, but I’d recommend labeling the subscript. The problem comes up when you have a dictionary that has an optional key: When you use “myDict[nil]”, you may get one or the other, but you probably mean one specifically.

I don’t think that’s an issue in the stdlib, because `Optional` doesn’t conform to `Hashable` and, AFAIK, no other stdlib types conform to `ExpressibleByNilLiteral`. Custom types could conform to both, though, and according to a playground, that does indeed lead to some confusing code:
struct Foo : ExpressibleByNilLiteral, Hashable {...}
extension Dictionary { subscript(_ key:Key?) -> Value? { return key != nil ? self[key!] : nil } }
var bar = [Foo:Int]()
bar[nil] //calls `Foo.init(nilLiteral:())`, and tries to look up the new `Foo` in `bar` using the stdlib's subscript
bar[nil as Foo?] //passes `Optional<Foo>.none, which uses the extension's subscript

I wouldn't be surprised if there's a proposal to make Optional conditionally conform to Hashable if/when we get conditional conformances.


(Hooman Mehr) #12

It is a hash, collision is fine. It is just a single value. I guess a hashValue of zero works well for `nil`.

···

On Dec 14, 2016, at 11:08 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:
I'll be curious as to how they propose we prevent collisions between Optional<T>.none.hashValue and any given T's hash value.