Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ListCollector #3745

Merged
merged 8 commits into from Feb 9, 2024
Merged

ListCollector #3745

merged 8 commits into from Feb 9, 2024

Conversation

nojaf
Copy link
Member

@nojaf nojaf commented Feb 8, 2024

An initial naive implementation for #3700.

I'm a little confused that FSharp.Core.CompilerServices.js looks fine:

import { addRangeInPlace } from "./Array.js";
import { toList } from "./Seq.js";
import { class_type } from "./Reflection.js";
export class ListCollector$1 {
    constructor() {
        this.collector = [];
    }
    Add(value) {
        const this$ = this;
        void (this$.collector.push(value));
    }
    AddMany(values) {
        const this$ = this;
        addRangeInPlace(values, this$.collector);
    }
    AddManyAndClose(values) {
        const this$ = this;
        addRangeInPlace(values, this$.collector);
        return toList(this$.collector);
    }
    Close() {
        const this$ = this;
        return toList(this$.collector);
    }
}
export function ListCollector$1_$reflection(gen0) {
    return class_type("Microsoft.FSharp.Core.CompilerServices.ListCollector`1", [gen0], ListCollector$1);
}
export function ListCollector$1_$ctor() {
    return new ListCollector$1();
}

but the usage in QuickTest.js does not:

import { some } from "./fable_modules/fable-library.4.11.0/Option.js";
import { defaultOf, equals } from "./fable_modules/fable-library.4.11.0/Util.js";
import { startsWith, isNullOrEmpty, toFail, printf, toConsole } from "./fable_modules/fable-library.4.11.0/String.js";
import { startImmediate } from "./fable_modules/fable-library.4.11.0/Async.js";
import { singleton } from "./fable_modules/fable-library.4.11.0/AsyncBuilder.js";
import { ListCollector$1__Close, ListCollector$1__Add_2B595 } from "./fable_modules/fable-library.4.11.0/FSharp.Core.CompilerServices.js";

// ...

testCase("meh2", () => {
    let copyOfStruct_1;
    const c = defaultOf();
    let copyOfStruct = c;
    ListCollector$1__Add_2B595(copyOfStruct, 1);
    (copyOfStruct_1 = c, ListCollector$1__Close(copyOfStruct_1, void 0));
});

Am I missing something obvious @MangelMaxime @ncave?

@MangelMaxime
Copy link
Member

This is probably because you used [<AttachMembers>] on the ListCollector type definition.

This tells Fable attach members to the JavaScript class instead of generated a function outside of the type like ListCollector$1__Add_2B595.

With [<AttachMembers>]

import { class_type } from "fable-library/Reflection.js";

export class ListCollector$1 {
    constructor() {
        this.collector = [];
    }
    Add(value) {
        const this$ = this;
        void (this$.collector.push(value));
    }
}

export function ListCollector$1_$reflection(gen0) {
    return class_type("Test.ListCollector`1", [gen0], ListCollector$1);
}

export function ListCollector$1_$ctor() {
    return new ListCollector$1();
}

Without [<AttachMembers>]

import { class_type } from "fable-library/Reflection.js";

export class ListCollector$1 {
    constructor() {
        this.collector = [];
    }
}

export function ListCollector$1_$reflection(gen0) {
    return class_type("Test.ListCollector`1", [gen0], ListCollector$1);
}

export function ListCollector$1_$ctor() {
    return new ListCollector$1();
}

export function ListCollector$1__Add_2B595(this$, value) {
    void (this$.collector.push(value));
}

If you remove [<AttachMembers>] it will probably works.

In general [<AttachMembers>] is only use for interop with JavaScript so I don't think we are using it in fable-library.

The alternative would be to specifically rewrite how Fable generates the code for this type/members.

For info using [<AttachMembers>] comes with limitations, as for example it does not support method overload because JavaScript method doesn't support it.

@nojaf
Copy link
Member Author

nojaf commented Feb 8, 2024

Removing those attributes doesn't help that much.
I believe something I'm not taking into account is that ListCollector is a struct in F# Core.

@ncave
Copy link
Collaborator

ncave commented Feb 8, 2024

@nojaf See PR#1

@nojaf nojaf marked this pull request as ready for review February 9, 2024 09:44
@nojaf
Copy link
Member Author

nojaf commented Feb 9, 2024

Thanks again @ncave for fixing this!
@MangelMaxime ready for review.

Comment on lines 21 to 26
let tests =
testList "ListCollector" [
testCase "ListCollector.Add and .Close" <| fun () ->
let result = cutOffLast [ 1; 2; 3 ]
result |> equal [ 1; 2 ]
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add tests for each method available and also edges cases like calling .close() on an empty ListCollector?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not overextend. I'm including only the parts that interest me, as this is largely experimental. The code is already solid—something we're both aware of—and this pull request improves its current state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree yes for this module it is probably enough, but it happens several times that doing so allowed us to catch behaviour we were not aware of.

There are still some APIs in fable-library that don't behave correctly because they didn't have a test associated for every usage. Having tests also allows us to catch on regressions.

I added the tests to cover the most common cases.

open Microsoft.FSharp.Core.CompilerServices

let cutOffLast list =
let mutable headList = ListCollector<'a>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code it seems like we can remove the mutable here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would not reflect the actual usage of shared code. In regular F# the collector needs to be mutable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know about that.

@MangelMaxime MangelMaxime merged commit e09608f into fable-compiler:main Feb 9, 2024
18 checks passed
@MangelMaxime
Copy link
Member

Thanks for the implementation, I will include it in the next release which should come out this weekend.

@nojaf
Copy link
Member Author

nojaf commented Feb 9, 2024

Thanks for going the extra mile here!

@ncave ncave mentioned this pull request Feb 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants