dart-lang / dart-lang/language

Issue with type inference in constant factory constructors

Open
#2,869 5 comments 0 reactions 0 assignees View on GitHub
request
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

Hi,

I found a possible issue while working with constant factory constructors (i.e. `const factory`) [^1].

[fpdart](https://pub.dev/packages/fpdart) defines a constant [factory constructor for the Option type](https://github.com/SandroMaglione/fpdart/blob/e892c8d158cc63c4e6d5b4eebaa95107c708d66a/lib/src/option.dart#L474) as follows:
```dart
/// Return a [None].
const factory Option.none() = None;

/// Return a `Some(a)`.
const factory Option.of(T t) = Some;
```

We found an issue when the type system tries to infer the generic type in some functions, for example [`map`](https://github.com/SandroMaglione/fpdart/blob/e892c8d158cc63c4e6d5b4eebaa95107c708d66a/lib/src/option.dart#L664):

```dart
Option map(B Function(T t) f) => const Option.none();
```

Returning a `const` using the factory constructor `none()` infers the type as `Option`. If instead the return is not marked as `const` the return type is `Option` as expected:

Screenshot 2023-02-26 at 18 02 19

Screenshot 2023-02-26 at 18 02 37

This causes **runtime** issues when working with the result of `map`. For example:

```dart
import 'package:fpdart/fpdart.dart';

Option nonEmptyString(String? s) => Option.fromNullable(s).map(
(t) => t.trim(),
);

void main() {
nonEmptyString(null).alt(() => nonEmptyString('hi'));
}
```

VSCode infers the return type as `Option`:

Screenshot 2023-02-26 at 18 15 39

but then when you run the app it fails with the following error:

```shell
Unhandled exception:
type '() => Option' is not a subtype of type '() => Option' of 'orElse'
#0 None.alt (package:fpdart/src/option.dart)
#1 main (file:///Users/sandromaglione/Development/projects/dart/fpdart/example/src/either/cast.dart:13:24)
cast.dart:13
#2 _delayEntrypointInvocation. (dart:isolate-patch/isolate_patch.dart:297:19)
#3 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
```

This happens because `map` in the function `nonEmptyString` returns `Option`, that fails when using [`alt`](https://github.com/SandroMaglione/fpdart/blob/e892c8d158cc63c4e6d5b4eebaa95107c708d66a/lib/src/option.dart#L676) (which calls `orElse` as reported in the error) with a type error.

Is this behaviour expected using `const factory`? Could there be an issue with inference?

[^1]: [PR](https://github.com/SandroMaglione/fpdart/pull/92)

Contributor guide

Open the contributing guide

Research direction

Start with the constant factory constructor examples in the issue and compare the inferred types of the const and non-const returns in Option.map. Then trace how the result is used by Option.alt and its reported orElse failure. Done means determining whether this inference is expected and documenting or resolving the language behavior with a corresponding reproduction.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.