Skip to content
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

Implicit interface implementation from an object expression #555

Closed
4 of 6 tasks
ntwilson opened this issue Mar 27, 2017 · 10 comments
Closed
4 of 6 tasks

Implicit interface implementation from an object expression #555

ntwilson opened this issue Mar 27, 2017 · 10 comments

Comments

@ntwilson
Copy link

ntwilson commented Mar 27, 2017

I propose we allow an object expression to satisfy an interface implicitly, given some object that already implements all of the members given in the interface. For example:

type IContainer<'t> = 
	abstract member Contains : 't -> bool

let helloWorld = "hello world!"
let xs = ResizeArray [1 .. 5]

let container1 = { new IContainer<string> helloWorld }
let container2 = { new IContainer<int> xs }

The syntax here is unimportant, I just went with the most minimal syntax possible.
I believe this would be related to suggestion #195 for normal implicit interface implementation in classes, but I haven't actually looked at the F# compiler source.

(If anyone has used impromptu-interface, this would allow you to do something similar, but would be statically checked by the compiler for correctness).

The existing way of approaching this problem in F# is to list out each member in the object expression, e.g.

type IContainer<'t> = 
	abstract member Contains : 't -> bool

let helloWorld = "hello world!"
let xs = ResizeArray [1 .. 5]

let container1 = { new IContainer<string> with member this.Contains el = helloWorld.Contains el }
let container2 = { new IContainer<int> with member this.Contains el = xs.Contains el }

For this example, this is rather trivial, but when dealing with interfaces with many members, it can get cumbersome quickly.

Alternatively, you could use Reflection a la impromptu-interface, but this forgoes any type checking.

Pros and Cons

The advantages of making this adjustment to F# are

  • less code to maintain when generating an interface and "wrapper" type for a type from a third-party library (for mocking, etc.)
  • reasonable generic mitigation for the problem of needing a system type to implement an additional interface, such as having List<'t> implement IReadonlyCollection<'t> in F# <= v4.0

The disadvantages of making this adjustment to F# are

  • add confusion?
  • maybe someone can help identify disadvantages that I would not be aware of

Extra information

Estimated cost: I haven't actually cracked open the F# compiler source, but I would guess that if #195 were implemented, this would be a minor addition.

Related suggestions: #195

Affadavit (must be submitted)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I would be willing to help implement and/or test this
  • I or my company would be willing to help crowdfund F# Software Foundation members to work on this
@smoothdeveloper
Copy link
Contributor

@ntwilson this is interesting feature, could you precise what would happen in such case:

type IContainer<'t> = 
	abstract member DoesNotContain : 't -> bool

let helloWorld = "hello world!"

let container1 = { new IContainer<string> helloWorld }

I'd like to see how the compiler would help me fix my mistakes if I'm doing something stupid.

@ntwilson
Copy link
Author

Ideally, since the type of helloWorld (string) doesn't implement member DoesNotContain: string -> bool, the compiler would generate an error saying that helloWorld is not a valid implementation of IContainer<string> because of the missing member. (Similar to the sort of error you'd see in C# if you try to implicitly implement an interface with a class, and the class doesn't have all the correct members. If I add the IContainer<string> interface to a C# class that doesn't contain member DoesNotContain, it tells me: "MyClass does not implement interface member IContainer.DoesNotContain(string)").

@matthid
Copy link

matthid commented Mar 27, 2017

Kind of related #524

@dsyme
Copy link
Collaborator

dsyme commented Mar 28, 2017

I think this is very related too: #132

@rmunn
Copy link

rmunn commented Apr 17, 2017

Also kind of related: #561 since it calls for implicit implementation of an interface (specifically, IDisposable).

@dsyme
Copy link
Collaborator

dsyme commented Nov 16, 2017

I will close this as a duplicate of #524

@dsyme dsyme closed this as completed Nov 16, 2017
@robkuz
Copy link

robkuz commented Nov 16, 2017

I am not sure about #524 but does that suggestion and really capture what is proposed here?

@dsyme
Copy link
Collaborator

dsyme commented Nov 17, 2017

@robkuz You're correct. Though I think any feature we did would need to be a synthesis of #524, #132 and this.

@matthid
Copy link

matthid commented Jun 5, 2019

@dsyme Can you clarify why you reopened. What is not captured by #524? I don't see it, they look identical to me. Is it that this suggestion doesn't allow to use with so this suggestion is "smaller"?

Anyway, after looking at all the other suggestions I suggest to use the same syntax here (adding the by keyword):

let container1 = { new IContainer<string> by helloWorld }

@dsyme
Copy link
Collaborator

dsyme commented Jun 16, 2022

Closing in favour of #524 after all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants