Flow does not refine type parameter based on type being parametrized
- Dominant language
- Rust
- Stars
- 22.3k
- Forks
- 1.9k
- PR merge metrics
- No merged PRs in 30d
Description
I'm not sure what is right correct terminology to describe this limitation, but it a major limitation that prevents flow users from splitting data & functions operating on it in favor of types with methods. While it's fine in general it becomes pretty painful when data needs to be send across thread boundaries. Here is [an example code that does not work](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgByArgLYBGeATgCJ4DGcAJtQDwCGAXGAHZmVUAfGAC8YAN6owYANYBLHk24AiPhWrLUAX3TZ8YALLsslOoxZUOAGjDluAfnbCxk6fMUrSxysqtSwLObU3GbMbE7aurgEoRYczkT81LHhwgA+ht54KZYgNhGojDwAzhgBDGGiYKxwxBiCABQKOHXcpVQKAOY2gWFUIRVxtfUAlNzDosKuYMUIchj0ABZgDb0WAHTuTCMS-tL07MUEqklUytzT0vtwJWUAbuwwxARiOOxURwBi8OwYTTwtDAjPZXORQFYkdRUdZyYqfBTzPANB5PPAjHaXK7SKh4DDEKg8MAo54grHAYBYsAAPV4p3WYAAKotYWA9AQWQpGKQ3hg5OQYAQ5hhlsKCHgAB74egYPBMMA4vEE1nRUlXcmU4pwUjsnhcnl8gVgBSldg8Xm-OQ3MBwcEAA2GttVWjAeBgR12lKuwqoiDAAFEqD6qA1bYQ4GV2LSoWAACTiZp1LS24GenRYtNXA7u5ReEx4c4eylFUpEx7PKprJEJjA9QbUdaVqgpylglbEnUm3V4G3+wNwJuFz0K-GEvgwGCq6TqrE0scwelMllso3FI26rX6-mC+YixZiyUMGVy4dK5c2ydgadXTXatd6i1bted828q09+11R2pl1ugiYrEnoS7ZOiCGbSCwUDsMQMAYBcqp3HAchyqsdb9C63LYM2WLer6AZBiGACqPAyDwiCEo2sgKEwyagf4Og6EAA):
```js
/* @flow */
type NumberDecoder = {
kind: "number"
}
type MaybeDecoder = {
kind: "maybe",
decoder: Decoder
}
type Decoder = NumberDecoder | MaybeDecoder<*, a>
const decode = (input: string, decoder: Decoder): out => {
switch (decoder.kind) {
case "number": {
const value = parseFloat(input)
if (Number.isFinite(value)) {
return value
// ^ number. This type is incompatible with the expected return type
// some incompatible instantiation of `out`
} else {
throw Error(`Not a number ${input}`)
}
}
case "maybe": {
const value = decode(input, decoder.decoder)
if (value instanceof Error) {
return null
// ^ null. This type is incompatible with the expected return type of
// some incompatible instantiation of `out`
} else {
return value
}
}
default: {
void (decoder: empty)
throw Error(`Unknown decoder kind`)
}
}
}
```
- First error reports that `number` is incompatible with some instantiation of `out`, but that should not matter as long as it's compatible with the given instantiation of `out` and since `NumberDecoder` requires `out:number` number should be compatible with given instigation of `out`.
- Second error reports that `null` is incompatible with some instantiation of `out`, but here as well it should not matter as passed `decoder` is narrowed down to `MaybeDecoder<*, out>` and given the `MaybeDecoder` it should be clear that `out` is compatible with `null`.
Now there is a way to avoid these issues [by using class based approach](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAdgFwKYBOUAhgMZ5gAiepcAJoQDzEB8YA3qmGA7QwBTYADgFccALgDOOAtgDmASnHEwqAL7pSMYpMlgAciIC2AI0LU+TVmAxGhMPEby49F+kyzGzBNp24BrbDpxMAAiT1NCUK4eGndBLFEJaVksRXEI7w4Y7losaTAAN2IYEQoAXjAhYgJJPAAxeGIcBKSFHJsoMH5DSIIAOgxJeuwMfH5i0rwFBWzuefmCPBwRAiwikrLUAEg1MDwYOrmFsBwACwJEMABRAkuCfgADfTgcMBVMwjAAEnZhMTUj3aCw03A0GlQWh0egAssQAJ5mNwMAjMNi2eyOZw4VxxFGMAD81j8YECWGCYSMCLM0W4vHcBBCyKsLBi9IE-2SMnkSiJx1ycHyb0mZTAlXZeFaYgANKczkN+hKCMD5hguhNNhRsNJiFhyHAurd7rMSQslis1mBPDAYB09gcjqbFstVusRXg7TFwehNIKChKxWBGHAxCwpRIwCl5LKlUy8UwQzgWEowImxay6fGBhLw+0gA):
```js
/* @flow */
interface Decoder {
decode(input:string):a
}
class MaybeDecoder implements Decoder {
kind: "maybe"
decoder: Decoder
decode(input:string):?a {
const value = decode(input, this.decoder)
if (value instanceof Error) {
return null
} else {
return value
}
}
}
const decode = (input: string, decoder: Decoder): out =>
decoder.decode(input)
```
It would be really nice if there as a way to achieve the same result as in last example but without having to attach decode implementation to a variant of the decoder.
Contributor guide
Assessment
This issue has not been assessed yet.