-
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
Support implicit interface implementation #195
Comments
Wouldn't this lead to additional type checking ambiguity and so potentially break existing code? |
I agree that while the current approach is perfectly fine in purely (or mostly) functional scenarios, when moving parts of large enterprise solutions to F# there may be a lot of Interop involved and the current syntax seems unnecessarily verbose. As far as I can see I don't think this adds any ambiguity, while the new hypothetical code omits the explicit implementation, this implementation would still be there (implicitly), any checks would basically be performed against the same objects/functionality. Existing code can only call explicit implementations (it's the only thing available right now) and such implementations are by definition always there for existing code since they have to be explicitly defined in the code atm, I don't see any room for ambiguity but I'd like to hear other people's opinions. |
I'd like to have more examples, and wonder if other suggestions wouldn't intersect in some ways that would alleviate the kind of extra hoops to merge several interface calls at client site. Interface is going to get "overloaded" in future versions of the framework, and I like to have F# leaning toward privately implemented interfaces. #501 + something like type MyComp() =
interface IComparer as icomp with
// ...
interface IFoo as ifoo with
// ...
// publish members
members from icomp
members from ifoo so long there is no conflict, it would just compile by exposing the members publicly. |
@smoothdeveloper if I understand your suggestion correctly, this is an elegant approach, everything is explicit yet with minimal verbosity Edit: I do see a potential inconvenience though, if IFoo requires and implementation that's already provided when implementing IComparer, does it have to be also provided for IFoo? |
@lfr, #501 alleviates the pain of authoring multiple interfaces in a type while also using those locally in the implementation of the type. To also help on the client / caller side by exposing all members easily, it would require to add a syntax such as
expected usageopen System.Collections
type IFoo =
abstract member FooCompare : a: IFoo -> b: IFoo -> int
abstract member RegularCompare : a: obj -> c: obj -> int
let mkFoo = fun _ -> Unchecked.defaultof<_>
let foo = MyFoo(mkFoo)
foo.Compare(1, 1)
foo.FooCompare(mkFoo 1, mkFoo 1) light interface authoring mode:type MyFoo(foo: obj -> IFoo) =
interface IComparer as icomp with
member x.Compare(a, b) = Unchecked.defaultof<_>
interface IFoo as ifoo with
member x.FooCompare a b = a.FooCompare(foo a, foo b)
member x.RegularCompare a b = icomp.Compare(a,b)
members from icomp
members from ifoo currentlytype MyFoo(foo : obj -> IFoo) as this =
let ifoo = this :> IFoo
let icomp = this :> IComparer
interface IComparer with
member x.Compare(a, b) = Unchecked.defaultof<_>
interface IFoo with
member x.FooCompare a b = a.FooCompare (foo (box a)) (foo (box b))
member x.RegularCompare a b = icomp.Compare(a,b)
member x.Compare(a,b) = icomp.Compare(a,b)
member x.FooCompare a b = ifoo.FooCompare a b
member x.RegularCompare a b = ifoo.RegularCompare a b Would that make sense? |
It's probably a given but just to make sure, would the following work with aliases if type MyFoo(foo: obj -> IFoo) =
interface IComparer as icomp with
member x.Compare(a, b) = Unchecked.defaultof<_>
interface IFoo as ifoo with
member x.FooCompare a b = a.FooCompare(foo a, foo b)
member x.RegularCompare = icomp.Compare // ← this
members from icomp
members from ifoo |
@lfr, given this code type checks right now, I'd say yes: open System.Collections
type IFoo =
abstract member FooCompare : a: IFoo -> b: IFoo -> int
abstract member RegularCompare : a: obj * c: obj -> int
type MyFoo(foo : obj -> IFoo) as this =
let ifoo = this :> IFoo
let icomp = this :> IComparer
interface IComparer with
member x.Compare(a, b) = Unchecked.defaultof<_>
interface IFoo with
member x.FooCompare a b = a.FooCompare (foo (box a)) (foo (box b))
member x.RegularCompare(a,b) = icomp.Compare(a,b)
member x.Compare(a,b) = icomp.Compare(a,b)
member x.FooCompare = ifoo.FooCompare // can be done here
member x.RegularCompare = ifoo.RegularCompare // also here
let mkFoo = fun _ -> Unchecked.defaultof<_>
let foo = MyFoo(mkFoo)
foo.Compare(1, 1)
foo.FooCompare (mkFoo 1) (mkFoo 1)
foo.RegularCompare (mkFoo 1, 1) |
In this case, I am of the opinion that the combination of aliases together with this issue's suggestion is a solid improvement for Interop scenarios. Perhaps we if we consider all of these interface-related suggestions as one package the benefit to effort ratio makes it more appealing when prioritizing. |
I'd suggest going the other way around as #132 suggests. IMHO we should extend #132 to include this use-case - and completely replace this suggestion - considering that today I do not write code with casts as @smoothdeveloper has suggested. Instead I write: type MyFoo(foo : obj -> IFoo) as this =
member x.Compare(a,b) = Unchecked.defaultof<_>
member x.FooCompare a b = a.FooCompare (foo (box a)) (foo (box b))
member x.RegularCompare a b = x.Compare(a,b)
interface IComparer with
member x.Compare(a, b) = x.Compare(a,b)
interface IFoo with
member x.FooCompare a b = x.FooCompare a b
member x.RegularCompare a b = x.RegularCompare a b Which is already shorter and IMHO easier to follow. With extending #132 this could be simplified to type MyFoo(foo : obj -> IFoo) as this =
member x.Compare(a,b) = Unchecked.defaultof<_>
member x.FooCompare a b = a.FooCompare (foo (box a)) (foo (box b))
member x.RegularCompare a b = x.Compare(a,b)
interface IComparer by this
interface IFoo by this Which again is Imho easier to follow and more useful in general. The disadvantage is that you might need to add type-annotations (in this case The "only" thing we need to change in #132 is that it need to work on object structure and not on types (ie the compiler should check that the object after |
Honestly, I don't see the reason to have implicit implementation. I can't think of a case in which an instance passed in terms of an interface should be cast down to the concrete type. Isn't the whole idea behind Programming to the Interface to base programming logic on the objects interfaces, rather than on internal implementation details? It seems to me that F# promotes the compliance with the Dependency Inversion Principle: if If one really needs that, chances are the code can be refactored to be independent from the |
I'm going through very old design issues either marking them as approved or closing them. I am marking this one as declined, as ultimately I don't think this fits with the F# approach to object programming. |
Submitted by Mastr Mastic on 3/23/2014 12:00:00 AM
133 votes on UserVoice prior to migration
F# only supports explicit interface implementation with the price of unnecessary and excessive casting (also causes readability issues), potential confusion when working with other languages, and causes limitation with F#'s OOP that could be resolved without any major language change.
To expand on this, this post by Mauricio Scheffer describes the issue very well. In addition, I'm sure I could argue that people would prefer to just write
identifier.Member
rather than(identifier :> Type).Member
whenever the member is a signature provided by an interface. This repeats quite a bit on average and it is messy, to say the least.It should also be pointed out that even though F# is primarily functional (and interfaces are less of an issue), F# code is still being used from other languages, and also some domains and tasks could be easier to take on from an OO approach.(I am also finding very high limitations with using WPF & XAML with some MVVM approaches)
Currently the workaround would be to copy-paste the signatures for every member of an interface.
Original UserVoice Submission
Archived Uservoice Comments
The text was updated successfully, but these errors were encountered: