Fable
Fable copied to clipboard
F# 8 and static member in interface
Hi, @MangelMaxime I just wanted to know if there are any plans to support F# 8. Especially I'm interested in static member in interface. This would be I huge thing for the whole Fable/Feliz
community since we could build strong typing for base HTMLElement/React
+ any other custom component with list of props
as a param. Look at this example (@Zaid-Ajaj I think this could interesting for you):
open Browser.Types
open Fable.Core
open Feliz
open Fable.Core.JsInterop
// we want to distinguish between props of different components and all mkAttr() will return an extra generic arg
// in the alternate scenario we can omit this and reuse everywhere IReactProperty to have more flexibility
[<Erase>]
type IProp<'t> = interface end
module Interop =
let mkAttr<'comp> (name: string) (value: obj) = unbox<IProp<'comp>> (name, value)
let createElement (name: string) (props: IProp<'comp> list) = Interop.createElement name !!props
// MOCK: Typescsript for React has generic FocusEvent<T> type, but Browser.Types don't support it
type FocusEvent<'t> = {| target: 't|}
// base props interface
[<Interface>]
type HtmlAttributes<'comp, 'element> =
static member inline className(x: string) = Interop.mkAttr<'comp> "className" x
static member inline onFocus(x: FocusEvent<'element> -> unit) = Interop.mkAttr<'comp> "onFocus" x
// input props interface = Input + htmlAttributes
[<Interface>]
type input =
inherit HtmlAttributes<input, HTMLInputElement>
static member inline isChecked(x: bool) = Interop.mkAttr<input> "checked" x
[<Erase>]
type HTML =
// notice: IProp<input> list - strong type check!
static member inline input (props: IProp<input> list) = Interop.createElement "input" props
module Test =
let test =
HTML.input [
// both HtmlAttributes and input props are available
input.className "test"
input.isChecked true
// here we have a target of HTMLInputElement. no need to cast!
input.onFocus(fun x -> printf $"{x.target.``checked``}")
]
Currently, we can achieve a similar effect by using type-extensions
, however, this is not ideal - the user could be forced to import an extra thing to have all functions available (if there are in separated assemblies/files/namespaces
).
@lukaszkrzywizna It should already be supported in Fable v4.4.0 and above, try it.
Wow! It works! I tested before with fable 4.4
but it didn't work because previous version didn't use inline
. In that case, the compiler throws an error:
./StaticMember.fs(1,1): error EXCEPTION: Unexpected static interface/override call: FSharp8Lib.HtmlAttributes`2.className (L22,6-L22,28)
at Fable.Transforms.FSharp2Fable.Util.callAttachedMember(Compiler com, FSharpOption`1 r, Type typ, CallInfo callInfo, FSharpEntity entity, FSharpMemberOrFunctionOrValue memb) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.Util.fs:line 1825
at Fable.Transforms.FSharp2Fable.Util.makeCallWithArgInfo(IFableCompiler com, Context ctx, FSharpOption`1 r, Type typ, FSharpOption`1 callee, FSharpMemberOrFunctionOrValue memb, MemberRef membRef, CallInfo callInfo) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.Util.fs:line 2132
at Fable.Transforms.FSharp2Fable.Compiler.transformExpr@781-41.Invoke(Context ctx) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 781
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at Fable.Transforms.FSharp2Fable.Compiler.transformMemberValue(IFableCompiler com, Context ctx, String name, FSharpMemberOrFunctionOrValue memb, FSharpExpr value) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1237
at Fable.Transforms.FSharp2Fable.Compiler.transformMemberFunctionOrValue(IFableCompiler com, Context ctx, FSharpMemberOrFunctionOrValue memb, FSharpList`1 args, FSharpExpr body) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1379
at Microsoft.FSharp.Primitives.Basics.List.collectToFreshConsTail[T,TResult](FSharpFunc`2 f, FSharpList`1 list, FSharpList`1 cons) in /home/dev/Projects/fsharp/src/FSharp.Core/local.fs:line 434
at Microsoft.FSharp.Primitives.Basics.List.collect[T,TResult](FSharpFunc`2 f, FSharpList`1 list) in /home/dev/Projects/fsharp/src/FSharp.Core/local.fs:line 442
at Fable.Transforms.FSharp2Fable.Compiler.transformFile(Compiler com) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1996
at [email protected](Unit unitVar) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Cli/Pipeline.fs:line 123
at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in /home/dev/Projects/fsharp/src/FSharp.Core/async.fs:line 510
at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in /home/dev/Projects/fsharp/src/FSharp.Core/async.fs:line 112
./Program.fs(1,1): error EXCEPTION: Unexpected static interface/override call: FSharp8Lib.HtmlAttributes`2.className (L10,4-L10,26)
at Fable.Transforms.FSharp2Fable.Util.callAttachedMember(Compiler com, FSharpOption`1 r, Type typ, CallInfo callInfo, FSharpEntity entity, FSharpMemberOrFunctionOrValue memb) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.Util.fs:line 1825
at Fable.Transforms.FSharp2Fable.Util.makeCallWithArgInfo(IFableCompiler com, Context ctx, FSharpOption`1 r, Type typ, FSharpOption`1 callee, FSharpMemberOrFunctionOrValue memb, MemberRef membRef, CallInfo callInfo) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.Util.fs:line 2132
at Fable.Transforms.FSharp2Fable.Compiler.transformExpr@781-41.Invoke(Context ctx) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 781
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at [email protected](Unit unitVar0) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 16
at MonadicTrampoline.run[a](Thunk`1 _arg1) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/MonadicTrampoline.fs:line 8
at Fable.Transforms.FSharp2Fable.Compiler.transformMemberValue(IFableCompiler com, Context ctx, String name, FSharpMemberOrFunctionOrValue memb, FSharpExpr value) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1237
at Fable.Transforms.FSharp2Fable.Compiler.transformMemberFunctionOrValue(IFableCompiler com, Context ctx, FSharpMemberOrFunctionOrValue memb, FSharpList`1 args, FSharpExpr body) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1379
at Microsoft.FSharp.Primitives.Basics.List.collectToFreshConsTail[T,TResult](FSharpFunc`2 f, FSharpList`1 list, FSharpList`1 cons) in /home/dev/Projects/fsharp/src/FSharp.Core/local.fs:line 434
at Microsoft.FSharp.Primitives.Basics.List.collect[T,TResult](FSharpFunc`2 f, FSharpList`1 list) in /home/dev/Projects/fsharp/src/FSharp.Core/local.fs:line 442
at Fable.Transforms.FSharp2Fable.Compiler.transformFile(Compiler com) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Transforms/FSharp2Fable.fs:line 1996
at [email protected](Unit unitVar) in /Users/mmangel/Workspaces/Github/fable-compiler/fable/src/Fable.Cli/Pipeline.fs:line 123
at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in /home/dev/Projects/fsharp/src/FSharp.Core/async.fs:line 510
at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in /home/dev/Projects/fsharp/src/FSharp.Core/async.fs:line 112
Compilation failed
Is that expected?
@lukaszkrzywizna Not sure, don't you need static abstract
for interfaces?
https://github.com/fsharp/fslang-design/blob/main/FSharp-7.0/FS-1124-interfaces-with-static-abstract-members.md#implementing-iwsams
@ncave That's a separate thing. Look at the example code here. It probably won't work with fable.
@lukaszkrzywizna You're right, looks like it's not supported in Fable yet. Here is a smaller repro that works in .NET 8, but not in Fable (yet):
[<Interface>]
type ITesting =
static member Testing x = x
[<EntryPoint>]
let main _args =
let a = ITesting.Testing 5
printfn $"{a}"
0
Fortunately, the inline version works. This could be a potential workaround (for now). Thank you @ncave for your help!