I'm trying to reach this way of polymorphism in GO
type Discoverer interface {
Discover() string
}
type A struct {
}
func (obj A) GetTest() string {
return "im in A"
}
type B struct {
A
}
func (obj B) GetTest() string {
return "im in B"
}
func (obj A) Discover() string {
return obj.GetTest()
}
func main() {
a := A{}
b := B{}
fmt.Println(a.Discover())
fmt.Println(b.Discover())
}
Now I'm getting in output
im in A
im in A
So, my question is: It is possible to see in output
im in A
im in B
Without "override" Discover of B?
func (obj B) Discover() string {
return obj.GetTest()
}
Why? I've alot of small methods in struct (as classes) and Discover pretty the same for all structs, so I want to avoid copy&paste Discover in each struct(class)
go playground https://play.golang.org/p/nYc2hc3UbG
Thanks in advance!
No. You're embedding A in B. B has no definition for Discover, therefor the version on A is always invoked. That method has a receiver of type A and A has no awareness of B or the fact that it's embedded in B. Therefor A can only call it's own version of GetTest().
The only reason B satisfies the discover interface is because it has A embedded. It implements it indirectly. If you want the functionality on B, you have to define it on B. This isn't polymorphism at all, it's composition. B isn't an A, B has an A. If you want polymorphism, you use interfaces and implement them. B is a discoverable, but only because it has an A.
I would assume this is just for the example but you actually have no reason at all to even have this Discovery method. Your interface should just require GetTest instead. Don't confuse embedding with inheritance. There is no inheritance in Go. If you want polymorphic behavior you achieve it by implementing interfaces and you can't cut corners like this because the relationship between an embedded type and the embeddor is not one of inheritance, there is no base class or "overrides".
To add to #evanmcdonnal's point, embedding is neither inheritance nor polymorphism. Inheritance just happens to be a convenience such that you may get the implementation of an interface for free but in Go, its explicit. Embedding may seem like inheritance but is not, as #evanmcdonnal has pointed out.
Rather, you should separate the concepts polymorphism from inheritance and embedding and inheritance. The comparisons do not carry over from most object oriented languages. As an example, below would be an implementation of polymorphism. Notice that Discover takes an interface and A and B implements the interface, making the function itself polymorphic.
package main
import "fmt"
type A struct{}
type B struct{}
type Test interface{
GetTest() string
}
func (a *A) GetTest() string {
return "i am in A"
}
func (b *B) GetTest() string {
return "i am in B"
}
func Discover(t Test) string {
return t.GetTest()
}
func main() {
a := &A{}
b := &B{}
fmt.Println(Discover(a))
fmt.Println(Discover(b))
}
Related
Is there any syntax can make this work? I need a property can determine its type in the compile time.
protocol P {}
struct A: P {
var onlyAHas: String
}
struct B: P {
var onlyBHas: String
}
var ins1: any P = A()
var ins2: any P = B()
ins1.onlyAHas = "a only"
ins2.onlyBHas = "b only"
Before getting to the solution, let's break down what any means, and while we're at it, we'll include some as well:
When you write:
var ins1: any P = A()
You are telling the compiler that you want to use ins1 as P. It's the protocol oriented equivalent of this OOP code:
class Base {
var baseProperty: String? = nil
}
class Concrete: Base {
var concreteProperty: String? = nil
}
let obj: Base = Concrete();
obj.baseProperty = "Some value" // <-- This is fine
obj.concreteProperty = "Some value" // <-- This is an error
This code tells the compiler that obj is a Base. You can assign it from a Concrete, but because that's a subclass of Base, but obj is still known locally as a Base not as a Concrete, so it can't access the properties of Concrete that weren't inherited from Base.
It's the same in your example. ins1 is known locally as a P not as an A, and P doesn't have an onlyAHas property.
You'd get similar behavior with some instead of any. There are a few differences between the two, but let's just talk about the main one:
some tells the compiler that it will be a type that it can resolve to one specific concrete type, but that it should enforce the abstraction to the protocol in source code. This allows it to generate more efficient code internally, because knowing the concrete type allows the compiler to call the concrete's implementation directly instead of going through its protocol witness table, which is the protocol-oriented analog of a "vtable" in OOP, so the effect is like in OOP when the compiler devirtualizes a method call because despite the syntax, it knows the actual concrete type. This avoids the runtime overhead of dynamic dispatch while still letting you use the abstraction of the existential type... well it's more like it requires you to use the abstraction of the existential type than lets you, because from a source code point of view, the abstraction is enforced.
any also enforces the abstraction, but it goes the other way in terms of the kind of optimizations the compiler can do. It says that the compiler must go through the protocol witness table, because, as the keyword suggests, its value could be any concrete type that conforms to the protocol, even if the compiler could determine that it's actually just one specific type locally. It also allows relaxation of some rules regarding using the protocol as a type when it has Self and associatedtype constraints.
But either way, you are telling the compiler that you want to use ins1 as a P and not as an A.
The solutions
There are a few solutions, actually:
Downcasting
The first is to downcast to the concrete type, as was suggested in comments by Joakim Danielson:
if var ins1 = ins1 as? A {
ins1.onlyAHas = "a only"
}
Downcasting is a code smell, but sometimes is actually the clearest or simplest solution. As long as it's contained locally, and doesn't become a wide-spread practice for using instances of type, P, it might be fine.
However, that example does have one problem: A is a value type, so the ins1 whose onlyAHas property is being set is a copy of the original ins1 you explicitly created. Having the same name confuses it slightly. If you only need the change to be in effect in the body of the if, that works just fine. If you need it to persist outside, you'd have to assign back to the original. Using the same name prevents that, so you'd need to use different names.
Execute concrete-specific code only at initialization
This only applies if the concrete type just configures some things for the protocol up-front, and thereafter protocol-only code can be used:
var ins1: any P = A(onlyAHas: "a only")
// From here on code can only do stuff with `ins1` that is defined in `P`
Or your could delegate the initialization to a function that internally knows the concrete type, but returns any P.
func makeA(_ s: String) -> any P
{
var a = A()
a.onlyAHas = s
return a
}
var ins1 = makeA("a only");
// From here on code can only do stuff with `ins1` that is defined in `P`
Declare protocol methods/computed properties that do the work.
This is the usual way to use protocols. Declaring a method in the protocol is similar to declaring a method in a base class. Implementing the method in a conforming concrete type is like overriding the method in a subclass. If you don't also provide a default implementation in a protocol extension, the protocol will enforce that conforming types implement the protocol - which is a big advantage over the OOP approach.
protocol P {
mutating func setString(_ s: String)
}
struct A: P
{
var onlyAHas: String
mutating func setString(_ s: String) {
onlyAHas = s
}
}
struct B: P
{
var onlyBHas: String
mutating func setString(_ s: String) {
onlyBHas = s
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
I'm doing this with a setString method, but you could certainly use a computed variable in the protocol to do the same thing, and that would be more "Swifty." I didn't do that just to emphasize the more general idea of putting functionality in the protocol, and not get hung up on the fact that the functionality in question happens to be setting a property.
If you don't need all conforming types to be able to set a String, one solution is to provide a do-nothing default implmentation in an extension on P:
protocol P {
mutating func setString(_ s: String)
}
extension P
{
mutating func setString(_ s: String) { /* do nothing */ }
}
// Same A and B definitions go here
struct C: P { }
var ins3: any P = C();
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
ins3.setString("c only") // <- Calls setString from extension of P
Most often though, setting/getting some concrete property is an implementation detail of doing some task that varies with the concrete type. So instead, you'd declare a method in the protocol to do that task:
protocol P
{
mutating func frobnicate()
}
struct A
{
var onlyAHas: String
mutating func frobnicate()
{
// Do some stuff
onlyAHas = "a only"
// Do some other stuff that uses onlyAHas
}
}
B would be defined similarly doing whatever is specific to it. If the stuff in comments is common code, you could break it down into prologue, main action, and epilogue.
protocol P
{
mutating func prepareToFrobnicate()
mutating func actuallyFrobnicate() -> String
mutating func finishFrobnication(result: String)
}
extension P
{
/*
This method isn't in protocol, so this exact method will be called;
however, it calls methods that *are* in the protocol, we provide
default implementations, so if conforming types, don't implement them,
the versions in this extension are called, but if they do implement
them, their versions will be called.
*/
mutating func frobnicate()
{
prepareToFrobnicate()
finishFrobnication(result: actuallyFrobnicate());
}
mutating func prepareToFrobnicate() {
// do stuff general stuff to prepare to frobnicate
}
mutating func actuallyFrobnicate() -> String {
return "" // just some default value
}
mutating func finishFrobnication(result: String) {
// define some default behavior
}
}
struct A
{
var onlyAHas: String
mutating func actuallyFrobnicate() -> String
{
// Maybe do some A-specific stuff
onlyAHas = "a only"
// Do some more A-specific stuff
return onlyAHas
}
}
struct B
{
var onlyBHas: String
mutating func actuallyFrobnicate() -> String {
"b only"
}
mutating func finishFrobnication(result: String)
{
// Maybe do some B-specific stuff
onlyBHas = result
// Do some more B-specific stuff
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.frobnicate();
ins2.frobnicate();
In this example, the frobnicate in the protocol extension is called, because it's defined only in the protocol extension.
For ins1, frobnicate then calls the extension's prepareToFrobnicate, because even though it's declared directly in the protocol, A doesn't implement that and a default implementation is provided in the extension.
Then it calls A's actuallyFrobnicate because it's defined directly in the protocol, and A does implement it, so the default implementation isn't used. As a result the onlyAHas property is set.
Then it passes the result from A's actuallyFrobnicate to the finishFrobnication in the extension, because it's defined directly in the protocol, but A doesn't implement it, and the extension provides a default implementation.
For ins2, frobnicate still calls the default prepareToFrobnicate, and then call's B's implementation of actuallyFrobnicate, but B's implementation doesn't set its onlyBHas property there. Instead, it just returns a string, which frobnicate passes to finishFrobnication, which calls B's implementation, because unlike A, B provides its own implementation, and that's where B sets it.
Using this approach, you can simultaneously standardize the general algorithm of a task like frobnicate, while allowing for dramatically different implementation behavior. Of course, in this case, both A and B just set a property in their respective concrete types, but they do it at different phases of the algorithm, and you can imagine adding other code, so that the two effects really would be very different.
The point of this approach is that when we call inst1.frobnicate(), it doesn't know or care about exactly what inst1 is doing internally do accomplish it. The fact that it internally sets the onlyAHas property in the concrete type is an implementation detail the calling code doesn't need to be concerned with.
Just use the concrete type
In your code example, you are creating and using ins1, and ins2 in the same context. So they could just as easily be defined like this:
var ins1 = A()
var ins2 = B()
ins1.onlyAHas = "a only" // <- This is fine because ins1 is an A
ins2.onlyBHas = "b only" // <- This is fine because ins2 is a B
If you have some function, munge that you want to do on both A and B, you can define it terms of the protocol.
func munge(_ p: any P)
{
// In here you can only use `p` as defined by the protocol, `P`
}
If munge needs to do things that depend on concrete-specific properties or methods, you can use one of the previously described approaches...
OR...
If you know for sure that you only will ever have a small number of concrete types conforming to P, which admittedly is sometimes impossible to really know, but occasionally you do, then you can just write specialized overloaded versions of munge for each concrete type:
func munge(_ a: A) {
// Do `A`-specific stuff with `a`
}
func munge(_ b: B) {
// Do `B`-specific stuff with `b`
}
This kind of regresses to older solutions to problems like this. When I say it's an old solution, I'm referring to the fact that even back when the C++ compiler was just a preprocessor that converted C++ source code to C source code which would then be compiled, didn't have templates, and standardization wasn't even on the horizon, it would let you overload functions. You can do that with Swift too, and it's a perfectly valid solution. Sometimes it's even the best solution. More often it leads to code duplication, but it's in your toolbox to use when it's appropriate.
New to Swift and trying to find nice ways of deciding which implementation of a protocol to use. Protocols with associated types don't seem to map onto generic interfaces in other languages at all. I thought something like the following would have worked.
protocol Foo {
associatedtype Item
}
class FooOffline<T>: Foo {
typealias Item = T
}
class FooOnline<T> : Foo {
typealias Item = T
}
func makeFoo<T, F: Foo>() -> F where F.Item == T{
return FooOnline<T>()
}
Am I going down the wrong path? Even if, could someone offer an explanation for why this wouldn't work?
I've got a toy example here that I can't find any solutions for online.
protocol Tree {
associatedtype Element
// some nice tree functions
}
class BinaryTree<T> : Tree {
typealias Element = T
}
class RedBlackTree<T> : Tree {
typealias Element = T
}
enum TreeType {
case binaryTree
case redBlackTree
}
// Use a generic `F` because Tree has an associated type and it can no
// longer be referenced directly as a type. This is probably the source
// of the confusion.
class TreeManager<E, F:Tree> where F.Element == E {
let tree: F
init(use type: TreeType){
// Error, cannot assign value of type 'BinaryTree<E>' to type 'F'
switch(type){
case .binaryTree:
tree = BinaryTree<E>()
case .redBlackTree:
tree = RedBlackTree<E>()
}
}
}
I'm not sure what the problem here is or what I should be searching for in order to figure it out. I'm still thinking of protocols as I would interfaces in another language, and I view a BinaryTree as a valid implementation of a Tree, and the only constraint on F is that it must be a Tree. To confuse things even more, I'm not sure why the following snippet compiles given that the one above does not.
func weird<F:Tree>(_ f: F){ }
func test(){
// No error, BinaryTree can be assigned to an F:Tree
weird(BinaryTree<String>())
}
Any pointers or explanations would be greatly appreciated.
I don't understand the context of the situation this would be in. However, I have provided two solutions though:
1:
class Bar<F:Foo> {
let foo: FooClass.F
init(){
foo = FooClass.F()
}
}
2:
class Bar<F:Foo> {
let foo: FooClass
init(){
foo = FooClass()
}
}
What you are currently doing doesn't make logical sense, to whatever you are trying to achieve
I don't know what are you trying for, but of course it's not possible to do that. from your example, you attempt to create Bar class with generic. and that not the appropriate way to create a generic object, because the creation of generic object is to make the object to accept with any type.
Here's some brief explanation of the generic taken from Wikipedia.
On the first paragraph that says, "Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters."
it's very clear about what are the meaning of to-be-specified-later, right :)
Back to your example :
class Bar<F: Foo> {
let foo: F
init() {
// Error, cannot assign value of type 'FooClass' to type 'F'
foo = FooClass()
}
}
From the above code, it is type parameter F which has a constraint to a type Foo. and, you try to create an instance for foo variable with a concrete implementation, that is FooClass. and that's not possible since the foo variable is a F type(which is abstract). of course we can downcast it, like this foo = FooClass() as! F, but then the foo is only limited to FooClass, so why even bother with generic then ?
Hope it help :)
Your approach to this is wrong. In your original example, you would have to specify the type of both the element (E) and the container (BinaryTree or RedBlackTree), in addition to the enum value. That make no sense.
Instead, you should construct the manager to take a tree as the constructor argument, allowing Swift to infer the generic arguments, i.e.
class TreeManager<E, F: Tree> where F.Element == E {
var tree: F
init(with tree: F) {
self.tree = tree
}
}
let manager = TreeManager(with: BinaryTree<String>())
Alternatively, you should look into using opaque return types in Swift 5.1 depending on what the final goal is (the example here is obviously not a real world scenario)
Something like this seems reasonable. The point was to try and have some piece of logic that would determine when the TreeManager uses one type of Tree vs another.
protocol TreePicker {
associatedtype TreeType : Tree
func createTree() -> TreeType
}
struct SomeUseCaseA<T>: TreePicker {
typealias TreeType = RedBlackTree<T>
func createTree() -> TreeType {
return RedBlackTree<T>()
}
}
struct SomeUseCaseB<T>: TreePicker {
typealias TreeType = BinaryTree<T>
func createTree() -> TreeType {
return BinaryTree<T>()
}
}
class TreeManager<T, Picker: TreePicker> where Picker.TreeType == T {
let tree: T
init(use picker: Picker){
tree = picker.createTree()
}
This introduces another protocol that cares about picking the tree implementation and the where clause specifies that the Picker will return a tree of type T.
I think all of this was just the result of not being able to declare the tree of type Tree<T>. Its a bit more verbose but its basically what it would have had to look like with a generic interface instead. I think I also asked the question poorly. I should have just posted the version that didn't compile and asked for a solution instead of going one step further and confusing everyone.
protocol Foo {
associatedtype F
}
class FooClass : Foo {
typealias F = String
}
class Bar<M:Foo> {
let foo: M
init(){
foo = FooClass() as! M
}
}
I have a protocol, let's say Fruit (see below).
Within one of the methods I want to use a custom struct.
This results in the following error:
Type 'Packet' cannot be nested in generic function 'saveObject()'
Why is this not allowed?
protocol Fruit: Codable
{
var vitamines: Int { get }
var description: String { get }
}
extension Fruit
{
func saveObject()
{
struct Packet
{
let val1, val2, val3: Int
}
let packet = Packet(val1: vitamines, val2: 0, val3: 0)
}
}
Looking for a solution or viable alternatives.
I've tried using a tuple but I need to save the Packet to Data, which is not easily possible using a tuple (as far as I know).
You cannot nest new types inside of generic structures in extensions this way. Allowing that would likely become very complex, since it is not quite clear whether this type would be Fruit.saveObject.Packet or <ConformingType>.saveObject.Packet. For example, consider the following (legal) code for how these kinds of types can escape, and the system has to deal with them, including knowing how to dispatch methods to them, how much storage they require, etc.
protocol P {}
func x() -> P {
struct T: P {}
return T()
}
type(of: x())
If you change this to make x() generic, then it is no longer legal:
func x<Y>() -> P {
struct T: P {} // error: type 'T' cannot be nested in generic function 'x()'
return T()
}
That said, if you believe that the language should be changed to allow this, then Swift Evolution is the process to suggest it. You should first think though how you would like this to work if Fruit had associatedtypes, if saveObject() were itself generic, and if Packet included reference to type variable defined in either of those places. (I'm not saying these are insurmountable problems at all. This may be an excellent feature and it may be possible to design it very well. You just need to think through how it interacts with other features of the language.)
The solution is to move Packet to the top level, outside the extension and outside the protocol.
I wanted to give the Functional Core/Imperative Shell approach a shot, especially since Swift's structs are so easy to create.
Now they drive me nuts in unit tests.
How do you unit test a net of value objects?
Maybe I'm too stupid to search for the right terms, but in the end, the more unit tests I write, the more I think I should delete all the old ones. In other words my tests seem to bubble up the call chain as long as I don't provide mock objects. Which is impossible for (struct) value objects, because there's no way to replace them with a fake double unless I introduce a protocol, which doesn't work well in production code when methods return Self or have associated types.
Here's a very simple example:
struct Foo {
func obtainBar() -> Bar? {
// ...
}
}
struct FooManager {
let foos: [Foo]
func obtainFirstBar() -> Bar {
// try to get a `Bar` from every `Foo` in `foos`; pass the first
// one which isn't nil down
}
}
This works well with a concrete Foo class or struct. Now how am I supposed to test what obtainFirstBar() does? -- I plug in one and two and zero Foo instances and see what happens.
But then I'm replicating my knowledge and assertions about Foo's obtainBar(). Well, either I move the few FooTests into FooManagerTests, which sounds stupid, or I use mocks to verify the incoming call instead of merely asserting a certain return value. But you can't subclass a struct, so you create a protocol:
protocol FooType {
func obtainBar() -> Bar
}
struct Foo: FooType { /* ... */ }
class TestFoo: FooType { /* do some mocking/stubbing */}
When Bar is complex enough to warrant its own unit tests and it seems it should be mocked, you end up with this instead:
protocol FooType {
typealias B: BarType
func obtainBar() -> B
}
But then the compiler will complain that FooManager's collection of foos doesn't work this way. Thanks, generics.
struct FooManager<F: FooType where F.B == Bar> {
let foos: [F] // ^^^^^^^^^^^^^^^^ added for clarity
func obtainFirstBar() -> Bar { /* ... */ }
}
You can't pass in different kinds of Foo, though. Only one concrete FooType is allowed, no weird mixes of BlueFoo and RedFoo, even if both return the same Bar and seem to realize the FooType protocol in the same way. This isn't Objective-C and duck typing isn't possible. Protocols don't seem to add much benefit in terms of abstraction here anyway unless they're not depending on anything else which is a protocol or Self.
If protocols lead to confusion and not much benefit at all, I'd rather stop using them. But how do I write unit tests if one object is mostly a convoluted net of dependent objects, all being value types, seldomly optional, without moving all tests for every permutation in the test suite of the root object(s)?