Skip to content

CLIEvent and Object expressions #3039

Closed

Description

@dbrattli

Description

The CLIEvent property seems to be missing from object expressions.

Repro code

open System

type InterfaceWithCLIEvent<'t> =
    [<CLIEvent>]
    abstract Event : IEvent<System.Action<obj,'t>,'t>


let event = Event<Action<obj, bool>,bool>()
let ifaceWIthEvent =
    { new InterfaceWithCLIEvent<_> with
        [<CLIEvent>]
        member __.Event = event.Publish }

Expected and actual results

In JavaScript this compiles to:

import Event$ from "fable-library/Event.js";

export const event = new Event$();

export const ifaceWIthEvent = {
    add_Event(handler) {
        event.Publish.AddHandler(handler);
    },
    remove_Event(handler_1) {
        event.Publish.RemoveHandler(handler_1);
    },
};

So the Event property is gone. I came across this issue since Python uses interfaces and I get the error: Can't instantiate abstract class ObjectExpr0 with abstract method Event

Interface looks like this:

class InterfaceWithCLIEvent_1(Protocol, Generic[_T]):
    @abstractmethod
    def add_event(self, __arg0: Any) -> None:
        ...

    @property
    @abstractmethod
    def Event(self) -> IEvent_2[Any, _T]:
        ...

    @abstractmethod
    def remove_event(self, __arg0: Any) -> None:
        ...

So the Event is part of the interface, but not part of the implementation. If you implement a class using the interface, then the property will be available.

Related information

  • Fable version: repl4
  • Operating system

Activity

  1. alfonsogarciacaro commented on Aug 8, 2022

    @alfonsogarciacaro
    Member

    Thanks for reporting @dbrattli! I need to have a look. Actually I never wanted to add Events to Fable, because I like Observables much better (current support has been mostly done by Krzystof and ncave) so I don't remember all the fitting parts 😅

  2. dbrattli commented on Aug 8, 2022

    @dbrattli
    CollaboratorAuthor
  3. alfonsogarciacaro commented on Aug 8, 2022

    @alfonsogarciacaro
    Member

    Hmm, that's the "trimmed down FCS" created by @ncave that we use to self-compile Fable.Standalone. But Fable.Cli doesn't use fable-fcs but a custom build also from ncave's FCS fork (service_slim branch). If we need to generate some code for object expressions implementing Events I assume we have to do it either here (for Fable AST) or here (for Babel/JS AST).

  4. dbrattli commented on Aug 8, 2022

    @dbrattli
    CollaboratorAuthor

    The problem is that the main property seem to have been removed, so it's not that easy to know that you have to re-add it just by looking for the _remove and _add methods. Anyways, consider this low-priority. I'm not using it for anything, just porting more tests to Python.

  5. ncave commented on Aug 8, 2022

    @ncave
    Collaborator

    @dbrattli I'm no expert, but it looks like this is how F# compiler (FCS) works for [<CLIEvent>] properties. In any case, it is not something that Fable is doing or changing.

    Can you perhaps just not output (in the interface) any property with [<CLIEvent>] attribute, instead of trying to re-add the property in the object expression? (Technically that should be done for all languages that emit interfaces, so in Rust too).

  6. ncave commented on Aug 8, 2022

    @ncave
    Collaborator

    @dbrattli @alfonsogarciacaro Looks like Dart also generates interfaces, so it has to be fixed there too (to not output [<CLIEvent>] properties in the interface).

  7. github-actions commented on Apr 3, 2026

    @github-actions
  8. added a commit that references this issue on Apr 8, 2026
    8cfacdc
  9. github-actions commented on Apr 8, 2026

    @github-actions
    Contributor

    🤖 This is an automated response from Repo Assist.

    I've implemented a fix for this issue and submitted a draft PR.

    Root cause: transformInterface in Fable2Python.Transforms.fs generates a Protocol class for each F# interface. FCS exposes a [<CLIEvent>] member as three symbols — the event property (e.g. SomethingChanged), add_SomethingChanged, and remove_SomethingChanged. All three pass the existing not memb.IsProperty filter, so all three become abstract Protocol members. But transformObjectExpr only generates add_*/remove_* methods — never the event property override. This leaves the Protocol with an unimplementable abstract member.

    Fix: After collecting interface members, detect event property members by checking whether a companion add_<name> member exists in the same interface. If so, exclude the event property from the Protocol. The add_*/remove_* accessors are retained.

    This fix also applies the same filtering to the Dart transformInterfaceDeclaration, as requested by @ncave. The previously commented-out test "test Generic interface expression can have CLI events" in tests/Python/TestEvent.fs has been reinstated.

    Note

    🔒 Integrity filter blocked 52 items

    The following items were blocked because they don't meet the GitHub integrity level.

    • #4220 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4166 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4143 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4104 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4044 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4038 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #3290 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #2279 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #2154 list_pull_requests: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4375 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4326 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4224 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4176 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4094 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4082 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • #4080 search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
    • ... and 36 more items

    To allow these resources, lower min-integrity in your GitHub frontmatter:

    tools:
      github:
        min-integrity: approved  # merged | approved | unapproved | none

    Generated by Repo Assist · ● 10.5M · ◷

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions