"outer let" to limit scope of temporary definitions in a nicer way


(Amir Michail) #21

What about this instead for temporary definitions?

temp let t = ... {
  let x = f(t)
}

// t not accessible here but x is

NSLog("x=\(x)")

#22

I was considering them to be inferred at the point of first use, almost as if they were unconstrained generic values generated by calling an anonymous ()->T function. That’s probably not how it would actually be implemented, but insofar as type-checking the closure is concerned, I don’t see why there would be any sort of inherent problem with letting the parameter types be determined by the statement where each one is first used, as a byproduct of the type-checking.


(Jordan Rose) #23

That doesn't work so well if the first use is to call a method. :-/

I'm not saying you can't make a bunch of cases work. They may even be common cases. But you end up with a set of rules that feels more complicated and capricious than what we have today: "if it has multiple statements, it's treated as opaque".


#24

…but that already doesn’t work in the single-expression scenario:

let x = { $0.count }
// error: type of expression is ambiguous without more context

Perhaps another reasonably simple rule could be “closures with any unknown parameter types are treated as opaque”.

So it would only be the return type that gets inferred.


(Jordan Rose) #25
let arrayOfArrays = [[1], [1, 2], [1, 2, 3]]
let x = arrayOfArrays.map { $0.count }

EDIT: Sorry, I thought at first you were saying it could never work. Still, "closures with any unknown parameter types are treated as opaque" is still kind of, uh, opaque, because it's not always obvious why a parameter type is unknown.


#26

The parameter type there is known from context though. Only the return type remains to be inferred. So perhaps another even simpler way of wording the rule would be…

“The compiler attempts to infer the return type of all closures.”


(Jordan Rose) #27

I hate this example because it's so contrived, but…

let x: Float = [1, 2, 3].reduce(0) { $0 + $1 }

This is a case where the parameter types of the closure cannot be inferred from the rest of the expression; the return type is known first, and that tells you the input types.

Now, it's also a single-expression closure. The example that needs a multi-statement closure is even more contrived:

let x: [Float] = [1, 2, 3].mapWithoutChangingType {
  let result = $0
  return result + 1.0
}

But assuming mapWithoutChangingType existed, this would type-check today, and I'm not sure it would under a multi-statement closure type inference rule.


We're getting pretty far from the changes people want to talk about for let, so we should probably take this to another thread if you want to continue it.


#28

The connection to this thread is purely about inferring return types. So if we continue to say parameter types are not inferred for multiline closures, we can still consider inferring the return type. Then we could write things like:

let x = {
  let t = ...
  if foo(t) {
    ...
    return bar
  }
  ...
  return baz
}()