-
Notifications
You must be signed in to change notification settings - Fork 301
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
Python: typematching against an Interface leads to incorrect pattern matches #3972
Comments
As a note, Fable for JavaScript is not able to type check against an interface and generates export function typeMatchSomeBoxedObject(o) {
if (typeof o === "number") {
return 1;
}
else {
return 3;
}
} but Fable JavaScript generates a warning when type checking against an interface to inform the user. Going back to the Python target, looking at the code I suppose we should be able to type check against Looking online, I see that we could use |
JFC Here is a workaround we are using atm to solve this for both python and JS in https://github.com/CSBiology/DynamicObj Our usecase is that we want to call See also this REPL JS: [<Emit("""$0["System.ICloneable.Clone"] != undefined && (typeof $0["System.ICloneable.Clone"]) === 'function'""")>]
let implementsICloneable (o:obj) : bool =
jsNative
[<Emit("""$0["System.ICloneable.Clone"]()""")>]
let cloneICloneable (o:obj) : obj =
jsNative Python: [<Emit("""hasattr($0, 'System_ICloneable_Clone') and callable($0.System_ICloneable_Clone)""")>]
let implementsICloneable (o:obj) : bool =
nativeOnly
[<Emit("""$0.System_ICloneable_Clone()""")>]
let cloneICloneable (o:obj) : obj =
nativeOnly And the match case can then be conditionally transpiled: let f (o:obj) =
match o with
#if FABLE_COMPILER_JAVASCRIPT || FABLE_COMPILER_TYPESCRIPT
| o when FableJs.implementsICloneable o -> FableJs.cloneICloneable o
#endif
#if FABLE_COMPILER_PYTHON
| o when FablePy.implementsICloneable o -> FablePy.cloneICloneable o
#endif
#if !FABLE_COMPILER
| :? System.ICloneable as clonable -> clonable.Clone()
#endif
| _ -> failwith "nah" |
I was able to have a look at this issue and it seems like we could make Python target support type check against interfaces by generating: @runtime_checkable
class IInterface(Protocol):
pass However, when using
Because of that: @runtime_checkable
class IInterface(Protocol):
@property
@abstractmethod
def LOL(self) -> int:
...
@runtime_checkable
class AnotherSuperbInterface(Protocol):
@property
@abstractmethod
def LOL(self) -> int:
... are both equivalent. structural typing is actually similar to what you are doing @kMutagene in your solution as in you are looking if the object has the shape that you are looking for. I see several solutions:
My personally opinion is that 3 is the best solution but 2 is also acceptable. I think that allowing interface type checking to work similarly as in Python offers new possibility to code compared to not allowing it at all. If we go with solution 2, we could like for JavaScript generate a warning explaining that type checking for interface is done via structural typing, blabla... |
Hey @MangelMaxime thanks for the update. It is great that this can be implemented to work out-of-the box in python, and i'd agree that solution 3 sounds best, the only issue i'd have with that is that the unique props could clutter types implementing many interfaces real fast, and look weird on intellisense/tooltips in native python. However, i did not recognize that JS simply does not create any match case at all, since our tests failed only for python i was assuming that this is a python only issue. Sadly this means for us that we still have to resort to a custom emit-based implementation, since the package we are creating is intended to be usable in native code in .NET, JS and py, even when this issue would be fixed for python. |
I spend some time this afternoon trying to implements solution 3 and manage to make it work somewhat with others issues popping up 😅 Pure interface testing works but when trying to type test using interface against class it was failing. Indeed, Fable (Python) doesn't make the class inherit from the interface: Fable/src/Fable.Transforms/Python/Fable2Python.fs Lines 3666 to 3676 in 53fda11
I tried to disable this and now the specific use case from this issue works, but like implied in the comment linked above the Python to resolve the code:
I think for now, I am going to try to make Python emit the same code as JavaScript. And in the future, we can try to revisit interface Type checking to make them work fully. It seems like there are some potential to make them work but I currently don't know well enough Python and the generated Python code by Fable. My experimentation can be found in https://github.com/fable-compiler/Fable/tree/experimentation/python/interface_type_checking cc @dbrattli In case, you have some idea or information to share regarding the current issue. |
@kMutagene If you see this warning in Fable output:
it means that you are type testing against an interface and that the test will always fail at runtime equal to I don't know why this is a warning only and not an error / compilation failure. |
Description
It seems like, when typematching against an interface, incorrect match result code is generated.
Repro code
See REPL
or transpile this code:
the resulting python code:
Expected and actual results
Expected: A type that does not implement the interface does not match the second match case and therefore 3 is returned via wildcard match case. This is what happens when executing the F# code
Actual: 2 is returned, as the wildcard match case is not generated
Related information
The text was updated successfully, but these errors were encountered: