-
Notifications
You must be signed in to change notification settings - Fork 21
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
Make it easier to declare external types #1054
Comments
I think .fsi approach would be idiomatic and won't have any unwanted effects. |
This could also play together with Reference Assemblies. A suggested spec could be:
|
I haven't done that much interop in Fable, but WebSharper does this by providing the following in its standard library: type StubAttribute() = inherit Attribute()
exception ClientSideOnly
let X<'T> = raise ClientSideOnly The WS compiler recognizes and processes [<Stub>]
type Foo(x: int) =
member _.Foo(x: string, y: float) = X<int>
member _.Value = X<int>
static member AsBar(foo: Foo) = X<Bar> I think a standardized "extern" syntax would add one main advantage, which is that the stock F# compiler would be able to detect and reject external calls, whereas .NET calls to |
Will static interface methods fix this problem? |
Some technical questions
Can you explain more why a solution like
Why wouldn't we fail at the definition point when emitting a normal .NET assembly? There's no point in declaring such definitions when emitting a normal .NET assembly? |
You are right, it probably doesn't make sense. I was thinking about the case where you might want to use a single fsproj in both .net and fable, and how to make that as seamless as possible, but I guess then the external types would need to be |
Sorry for the late reply!
I'm assuming in the assembly to which the source file including the declaration belongs. In the case of Fable, we usually decorate these types with an attribute
Yes, I'm mainly thinking in F# compilers targeting other platforms than .NET (although maybe this could also be used to declare external code from C++/Java/Swift when using .NET mobile?) I guess it's ok to fail immediately when trying to build for .NET, although I know of some devs that use
Sorry I was not clear enough. @Tarmil solution does indeed work and we already do something similar with the But yes, maybe we should just recommend to use classes with dummy implementations from now on and provide a script or an IDE tool to automatically convert current bindings. There are already some discussions about this in the Fable repo 😅 @0x53A Didn't know about the reference assemblies proposal, thanks for pointing that out! I think this could be beneficial for assemblies containing only bindings as with the Fable.Browser.* packages. although I would still like to have a way to easily declare bindings directly in source code. Not sure what would be the speed gain when loading these assemblies if we can skip the implementation data. But this is actually important for the REPL which does download assemblies and we try to reduce their size as much as possible. In fact @ncave has a branch of the F# compiler that (I guess) does basically that: emit assemblies by keeping only the metadata, and we use it to build the F# assemblies we have in the REPL.
@charlesroddie I would need to check the static interface methods proposal, but I think it would probably not be enough because you still need an implementing type to know the location of the static method. |
Another issue that obfuscates the syntax of empty classes is the fact that virtual methods are quite verbose. I believe that this is to discourage the use of virtual methods, but in JS you could say methods are virtual by default, and overriding them is a common pattern (for example when declaring web components), so most of the times we would have to duplicate all methods open Fable.Core
[<ImportMember(from="my-pkg")>]
type Parent() =
member _.Bar: string = jsNative
abstract Foo: string -> string
default _.Foo(x: string): string = jsNative
type Child() =
inherit Parent()
override this.Foo(x) = base.Foo(x) + this.Bar Maybe allowing the empty classes (without implementation) just for bindings would allow us to enable |
Just to say I can't see a concrete suggestion here that isn't sufficiently covered by using an attribute and a class with dummy implementations. The dummy implementations aren't so hard to write or generate, and saves us needing a way to specify what code gets generated if compiled as .NET code. I'll mark this as "probably not" for these reasons. (However the enthusiastic number of votes makes it feel like there's some need here not being met, so I'm aware we may want to revisit this) |
You're right @dsyme. After writing bindings for the new compilation targets in Fable 4 I can do most of the things with dummy implementations so probably it's not worth to add more compiler machinery just for this. I will close the suggestion. Actually the main problem I'm having right now is the lack of support of optional arguments in module functions. Because in languages like Python/JS/Dart modules/files can contain both classes and functions with optional arguments, I need to have a separate module (to host the classes) and a class (to host the functions with optional arguments as static members), which makes it complicated to simulate the structure of the imported library. It does work if I always qualify the module/class because F# inserts the I think I'm to blame for the upvotes because at some point I asked people to upvote this to improve Fable bindings in Twitter 😅 |
Thanks @alfonsogarciacaro Could you generate static members and use AutoOpen on the class? |
@dsyme Oh my! For some reason, I believed that |
I propose we make it easier to declare external types. As F# is being used in more runtimes beyond .net we need a way to easily declare types from external languages. This is a paint point in Fable right now. Currently we use interfaces to avoid the limitations of F# classes and the ugly syntax with dummy implementations, but this requires us to declare the type in 3 parts (one interface for instance members, another for static members and a module value to access the static part) besides making it not possible to use type constructors.
I can think of two possible solutions but other suggestions are welcome:
Please note this suggestions is NOT about altering the F# type system (e.g. adding structural typing) and it's not about Typescript/JS in particular. It's about enabling some syntax to declare types (classes) as they're currently possible in F# but without implementation. Calls to members of these types won't trigger compilations errors and will appear normally in the typed AST (when compiling to .NET IL dummy classes throwing runtime errors could be generated).
The text was updated successfully, but these errors were encountered: