Type inference of functions with more than one statement


(Ross LeBeau) #1

It seems that the compiler fails to infer the type of an anonymous function
if the function contains more than one statement. For example, this works:

let a = [[1,2],[3],[4,5,6]]
var b: [Int]
b = a.flatMap { elem in
  return elem
}

But this fails with an error "cannot convert return expression of type
'[Int]' to return type 'Int?'":

b = a.flatMap { elem in
  print(elem)
  return elem
}

And, of course, if you explicitly type the function, it works again:

b = a.flatMap { (elem: [Int]) -> [Int] in
  print(elem)
  return elem
}

Greg Titus informed me that this is due to a heuristic in the compiler
where it only imports the outer context when type-checking anonymous
functions with a single statement. Before we knew this, I and others, found
it perplexing that out of two functions that return the same value, one
will compile and the other will not.

Are there any plans to extend the context of type-checking in the case of a
failure like this? If not, is it something to consider? It seems like a
difficult problem to solve without rather advanced knowledge of what's
going on.

Also if this has already been brought up, forgive me, I just subscribed to
this list and a quick search of the archives didn't get any hits :slight_smile:

···

--
Ross


(Slava Pestov) #2

Hi Ross,

Swift's type inference operates at the level of a single statement. This is why we can infer parameter and return types for single-expression closures. While conceptually, it would not be a huge change to the type checker algorithm to support global type inference for closures and other functions consisting of multiple statements, we feel that philosophically, this is not a direction the language should take. Global type inference will further exacerbate performance problems with type checking, as well as make it harder to produce good diagnostics.

So really, this has less to do with closures per se, and more with just the general design and philosophy of the type checker.

(Please correct me if I've mis-represented anything here -- this is my recollection of listening in on various discussions in the past).

···

On Jul 8, 2016, at 10:00 AM, Ross LeBeau via swift-dev <swift-dev@swift.org> wrote:

It seems that the compiler fails to infer the type of an anonymous function if the function contains more than one statement. For example, this works:

let a = [[1,2],[3],[4,5,6]]
var b: [Int]
b = a.flatMap { elem in
  return elem
}

But this fails with an error "cannot convert return expression of type '[Int]' to return type 'Int?'":

b = a.flatMap { elem in
  print(elem)
  return elem
}

And, of course, if you explicitly type the function, it works again:

b = a.flatMap { (elem: [Int]) -> [Int] in
  print(elem)
  return elem
}

Greg Titus informed me that this is due to a heuristic in the compiler where it only imports the outer context when type-checking anonymous functions with a single statement. Before we knew this, I and others, found it perplexing that out of two functions that return the same value, one will compile and the other will not.

Are there any plans to extend the context of type-checking in the case of a failure like this? If not, is it something to consider? It seems like a difficult problem to solve without rather advanced knowledge of what's going on.

Also if this has already been brought up, forgive me, I just subscribed to this list and a quick search of the archives didn't get any hits :slight_smile:

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


(Ross LeBeau) #3

Hi Slava,

Thanks for the insight. This is what I suspected, but I wasn't sure about
how set the team was on this concept. Currently, it seems that if the types
cannot be inferred, the type checker picks an implementation and checks
against that.

E.g., the following code works because it conforms to the signature of the
flatMap that the type checker decided to use:

b = a.flatMap { elem in
  print(elem)
  return 1
}

Given that adding multi-statement type inference is off the table, perhaps
you could consider adding a helpful message when this situation arises?
Like if type inference of a closure fails, and the closure doesn't type
check with the assumed context, maybe tell the user "Did not infer types,
try explicitly declaring them"?

Just trying to think of a way to guide a less advanced user, since this
error could arise from many functions and also from genuine errors, so it
may be hard to google/ask for help with.

···

--
Ross LeBeau

On July 8, 2016 at 2:31:46 PM, Slava Pestov (spestov@apple.com) wrote:

Hi Ross,

Swift's type inference operates at the level of a single statement. This is
why we can infer parameter and return types for single-expression closures.
While conceptually, it would not be a huge change to the type checker
algorithm to support global type inference for closures and other functions
consisting of multiple statements, we feel that philosophically, this is
not a direction the language should take. Global type inference will
further exacerbate performance problems with type checking, as well as make
it harder to produce good diagnostics.

So really, this has less to do with closures per se, and more with just the
general design and philosophy of the type checker.

(Please correct me if I've mis-represented anything here -- this is my
recollection of listening in on various discussions in the past).

On Jul 8, 2016, at 10:00 AM, Ross LeBeau via swift-dev < swift-dev@swift.org> wrote:

It seems that the compiler fails to infer the type of an anonymous

function if the function contains more than one statement. For example,
this works:

let a = [[1,2],[3],[4,5,6]]
var b: [Int]
b = a.flatMap { elem in
return elem
}

But this fails with an error "cannot convert return expression of type

'[Int]' to return type 'Int?'":

b = a.flatMap { elem in
print(elem)
return elem
}

And, of course, if you explicitly type the function, it works again:

b = a.flatMap { (elem: [Int]) -> [Int] in
print(elem)
return elem
}

Greg Titus informed me that this is due to a heuristic in the compiler

where it only imports the outer context when type-checking anonymous
functions with a single statement. Before we knew this, I and others, found
it perplexing that out of two functions that return the same value, one
will compile and the other will not.

Are there any plans to extend the context of type-checking in the case of

a failure like this? If not, is it something to consider? It seems like a
difficult problem to solve without rather advanced knowledge of what's
going on.

Also if this has already been brought up, forgive me, I just subscribed

to this list and a quick search of the archives didn't get any hits :slight_smile:

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


(Slava Pestov) #4

Hi Ross,

That's a good point. Do you mind filing a JIRA issue for this?

Slava

···

On Jul 8, 2016, at 11:56 AM, Ross LeBeau <ross.lebeau@gmail.com> wrote:

Hi Slava,

Thanks for the insight. This is what I suspected, but I wasn't sure about how set the team was on this concept. Currently, it seems that if the types cannot be inferred, the type checker picks an implementation and checks against that.

E.g., the following code works because it conforms to the signature of the flatMap that the type checker decided to use:

b = a.flatMap { elem in
  print(elem)
  return 1
}

Given that adding multi-statement type inference is off the table, perhaps you could consider adding a helpful message when this situation arises? Like if type inference of a closure fails, and the closure doesn't type check with the assumed context, maybe tell the user "Did not infer types, try explicitly declaring them"?

Just trying to think of a way to guide a less advanced user, since this error could arise from many functions and also from genuine errors, so it may be hard to google/ask for help with.

--
Ross LeBeau

On July 8, 2016 at 2:31:46 PM, Slava Pestov (spestov@apple.com <mailto:spestov@apple.com>) wrote:

Hi Ross,

Swift's type inference operates at the level of a single statement. This is why we can infer parameter and return types for single-expression closures. While conceptually, it would not be a huge change to the type checker algorithm to support global type inference for closures and other functions consisting of multiple statements, we feel that philosophically, this is not a direction the language should take. Global type inference will further exacerbate performance problems with type checking, as well as make it harder to produce good diagnostics.

So really, this has less to do with closures per se, and more with just the general design and philosophy of the type checker.

(Please correct me if I've mis-represented anything here -- this is my recollection of listening in on various discussions in the past).

> On Jul 8, 2016, at 10:00 AM, Ross LeBeau via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:
>
> It seems that the compiler fails to infer the type of an anonymous function if the function contains more than one statement. For example, this works:
>
> let a = [[1,2],[3],[4,5,6]]
> var b: [Int]
> b = a.flatMap { elem in
> return elem
> }
>
> But this fails with an error "cannot convert return expression of type '[Int]' to return type 'Int?'":
>
> b = a.flatMap { elem in
> print(elem)
> return elem
> }
>
> And, of course, if you explicitly type the function, it works again:
>
> b = a.flatMap { (elem: [Int]) -> [Int] in
> print(elem)
> return elem
> }
>
> Greg Titus informed me that this is due to a heuristic in the compiler where it only imports the outer context when type-checking anonymous functions with a single statement. Before we knew this, I and others, found it perplexing that out of two functions that return the same value, one will compile and the other will not.
>
> Are there any plans to extend the context of type-checking in the case of a failure like this? If not, is it something to consider? It seems like a difficult problem to solve without rather advanced knowledge of what's going on.
>
> Also if this has already been brought up, forgive me, I just subscribed to this list and a quick search of the archives didn't get any hits :slight_smile:
>
> --
> Ross
> _______________________________________________
> swift-dev mailing list
> swift-dev@swift.org <mailto:swift-dev@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-dev


(Ross LeBeau) #5

Filed here: https://bugs.swift.org/browse/SR-2033

Thanks for the help! Cool to learn a new thing about Swift today :slight_smile:

···

--
Ross LeBeau

On July 8, 2016 at 2:57:55 PM, Slava Pestov (spestov@apple.com) wrote:

"Did not infer types, try explicitly declaring them"