'static func' as alternative syntax for ()->()

Anyone interested in having this syntax:

func outer(){

    var foo = 123

    static func inner( _ boo: Bool ) -> Bool { 

        // scope doesn't expose 'foo' here

...serve as an alternative to this?

func outer(){

    var foo = 123

    let inner: (Bool)->Bool = { 

        // scope doesn't expose 'foo' here

In some situations it's convenient for a nested function to have access to the variables in its surrounding scope, but just as often it can allow bugs. This is especially the case when a programmer is weighing the possibility of moving the nested func elsewhere, after completing it, and has no guarantee that they didn't accidentally rely on a variable from the outer scope.

Maybe someone can suggest if the word 'static' is semantically inappropriate? It seems to fit, intuitively, but my intuition may be wrong.

This seems like it would be pretty easy to implement, and not cause any disruption to existing code (since it's illegal syntax at the moment). I use the syntax for closures this way sometimes, but it's a bit ugly, doesn't allow argument labels, and makes it less convenient to shuttle the function to a new home, if I decide I don't want it nested after all.

1 Like

I appreciate anything that can give compilation guaranties but isn't marking the function as pure what you are after. If so, I'd rather have some way to mark my function as pure which would work anywhere.

3 Likes

Yes, "pure" would work for me.

The reason I suggested 'static' is because it feels like a minor tweak (as far as I can tell, it's currently illegal to declare a nested function 'static'). Other than that, an all-encompassing "pure" modifier sounds better.

Whatever has the best chance of appealing to the Swift team and community is equally welcome to me.

Is this asking for effectively the same thing as this, or is there a difference between this and a kind of exclusionary capture list?

You can already do this today with @convention(thin) but it reads to sub-optimal error messages if you accidentally use a variable from the outer scope.

func f() {
  var x = 10
  let g : @convention(thin) () -> Int = { x }
  // ^ error: INTERNAL ERROR: feature not implemented: nontrivial thin function reference
  return g()
}
1 Like

@typesanitizer The language reference doesn't include @convention(thin), but @convention(c) gives a better error message.

func f() -> Int {
  let x = 10

  // error: a C function pointer cannot be formed
  // from a closure that captures context
  let g: @convention(c) () -> Int = { x }
  //                                ^

  return g()
}

Sure, for this case it works fine. From a semantic perspective, I think @convention(thin) matches the desired idea more closely (this is a Swift function that doesn't carry any context) and is also more flexible.

func f() {
  // OK
  let _ : @convention(thin) (inout Int) -> () = { _ in }
  let _ : @convention(thin) () throws -> () = { }


  let _ : @convention(c) (inout Int) -> () = { _ in }
  // ^ error: '(inout Int) -> ()' is not representable in Objective-C, so it cannot be used with '@convention(c)'
  let _ : @convention(c) () throws -> () = { }
  // ^ error: '() throws -> ()' is not representable in Objective-C, so it cannot be used with '@convention(c)'
}

That said, if there is interest in using it more, we should round out the feature and add it to the language reference when it works properly.

2 Likes

Thanks. While I was hopeful that @convention(thin), which I'd never heard of before your comment, would work the way I proposed, sadly, it appears it isn't actually legal syntax for nested functions, only closures.

The problem with closure syntax, in my view, is that it's somewhat ugly, and doesn't currently support named arguments.

Sure. But I think if there is one spelling already, there's a much higher bar for introducing an alternate spelling for the same thing. I think that a proposal to allow @convention in more places would have an easier time getting accepted compared to introducing a new keyword or repurposing an existing keyword in a different context.

Maybe you'd like to help out @Jumhyn to fix the named arguments part? :wink:

2 Likes