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

Type func signatures and interface bindings #24996

Closed
l0k18 opened this issue Apr 22, 2018 · 6 comments
Closed

Type func signatures and interface bindings #24996

l0k18 opened this issue Apr 22, 2018 · 6 comments

Comments

@l0k18
Copy link

l0k18 commented Apr 22, 2018

I have asked in a couple of other places but it occurred to me this might be a good place to ask, as maybe this is a 'bug' of some sort.

When defining a function type, viz:

type FunctionName func(Parametertype...) (Returntype...) 

the compiler flags a type mismatch if trying to assign a function that has otherwise identical signature except is prefixed with a binding to an interface. I could not find any information about specifying the binding type in this type of declaration. As such I worked out that it would be possible to implement a binding for a first class function by placing the primary code within one function, with the pointer to the type in the parameter list, and then calling this function, pretty much pass-through, as a wrapper, and then the function can be called as a child of the parent typed variable without having to explicitly pass the pointer to the struct or variable we want to bind to.

I am curious to know if there is some reason why this isn't in the syntax, or if simply I have gone a long way around to enable me to substitute a set of functions to operate on similar but comparable other types within a bigger library, with my hackish solution.

This is not a perfect example, as in fact in my code I also have a slice embedded into an interface{} variable in the struct, but this shows all the elements I am referring to and as to how I worked around this seeming limitation (I may just have not fully understood how composition and embedding work):

type nullTester func(*Bast, uint32) bool

type Bast struct {
  ...
  isNull    nullTester
  ...
 }

func isNull(b *Bast, d uint32) bool {
  return d == 0
}

func NewBast() (b *Bast) {
  ...
  b.isNull = isNull
  ...
}

// IsNull - tests if a value in the tree is null
func (b *Bast) IsNull(d uint32) bool {
  return b.isNull(b, d)
}

As you can see, if I have assigned this function to a variable in the struct, and the type bound function will be in the same scope, I should thus be able to override a function through the use of first class functions in this way. It doesn't really break any typing rules, it's just that the declaration at the top does not match the function at the bottom and thus I can't put b.isNull = IsNull as the compiler says it can't find the name.

@bronze1man
Copy link
Contributor

Do you mean b.isNull = (*Bast).IsNull ? It works. https://play.golang.org/p/Rgoc5zMezdb

@l0k18
Copy link
Author

l0k18 commented Apr 23, 2018

That's what I was looking for! whew I knew about the postfixed .(type) for type assertion but I didn't know there was a prefix of any form. Thanks!

@l0k18 l0k18 closed this as completed Apr 23, 2018
@l0k18
Copy link
Author

l0k18 commented Apr 23, 2018

Ok, I seem to have finally 100% fixed the issue. I need to not use the type Name func syntax at all. The slot in the struct needs to be typed as interface{}

Also, it appears that in the interface the signature for the function has to be public (initial capital) for this to work. Kinda makes sense since without public it's not able to export outside of the scope (I think).

Ok, I have confirmed that this seems to be the correct way to do it (at least go vet is happy):

type Bast struct {
  ...
  isNull    interface{}
  ...
 }

func (b *Bast) isNull(d uint32) bool {
  return d == 0
}

func NewBast() (b *Bast) {
  ...
  b.isNull = (*Bast).isNull
  ...
}

// IsNull - tests if a value in the tree is null
func (b *Bast) IsNull(d uint32) bool {
  return b.isNull(b, d)
}

@l0k18
Copy link
Author

l0k18 commented Apr 23, 2018

ok, I just want to leave this issue here, with a playground link that shows the first answer I proposed above is correct.

https://play.golang.org/p/FMvisWS9tuP

package main

import "fmt"

type nullTester func(*Bast, uint32) bool

type Bast struct {
  isNull    nullTester
}

func isNull(b *Bast, d uint32) bool {
  return d == 0
}

func NewBast() (b Bast) {
  b.isNull = isNull
  return
}

// IsNull - tests if a value in the tree is null
func (b *Bast) IsNull(d uint32) bool {
  return b.isNull(b, d)
}

func main() {
  b := NewBast()
  fmt.Println("is 0 == 0 ?", b.IsNull(0))
  fmt.Println("is 1 == 0 ?", b.IsNull(1))
}

I don't know if my solution is idiomatic, and I simply have not been able to understand how to bind different functions in. The question of how to bind a slice in is not the bit that is challenging, as it can be defined as interface{} type in the struct, and then in the functions .(*datatype), and I can then overload functions.

It's the fact that I have found a way to implement function overloading that makes me think this is not idiomatic but I see no way in which this violates type safety at all.

If the syntax for type func allowed the specification of a method binding, the resultant code would be a lot cleaner and lack this boilerplate wrapper that allows the passing of the bound type through the interface method, but if I may submit this as a proposal for a grammar extension I think this would in no way break Go 1.0 code, that I know of (of course I could be wrong, but extensions usually don't break things), but without this change this above is how to do function overloading in Go, at least one way anyway. I simply could not figure out or follow any of the methods I have seen that would work outside of a single file scope, and I don't think combining multiple different interface/class specs in one source file is either maintainable or flexible for implementing especially data types that need to work with arbitrary but analogous different data payloads.

@l0k18 l0k18 reopened this Apr 23, 2018
@myitcv
Copy link
Member

myitcv commented Apr 23, 2018

@l0k1verloren I'm unclear from reading the above whether you think there is a problem here, or whether this is more of a discussion. I can't see an issue with the code you just posted; it functions as expected.

Please can I ask you to continue the discussion outside the issue tracking system? The Go project does not use its bug tracker for general discussion or asking questions. The Github bug tracker is only used for tracking bugs and proposals going through the Proposal Process. The questions wiki page has a good list of places for everything else.

Thanks

@myitcv myitcv closed this as completed Apr 23, 2018
@l0k18
Copy link
Author

l0k18 commented Apr 23, 2018

Simply that because I can't add a method binding to a type Name func required me to write the hack using a first class function (otherwise, apart from the implied passing of the pointer to the struct) matching the typespec, stored in the type struct, and invoke it from the actual type bound function.

The type syntax lacks the binding part that causes otherwise identical function signature to be 'not found'. There is no reason I can think of except OOP-phobia, and as you can see by my code, it works, it's not violating any type safety, and it's a useful construction for being able to generalise small parts of libraries to deal with a variety of types (it came up for me because I was working on a binary search tree, a clear case for the need of polymorphism to not require so much boilerplate).

It's a side thing but I also think the special exception for slices that do not behave consistently compared to every other Go type. I had to encapsulate a slice inside a struct in order to allow embedding into one library the ability to change a back-store. This is just a limited case, and very inconsistent, and very much in line with the subject of this issue as well. It does not in fact stop polymorphism, it just makes it cumbersome to implement, for absolutely no reason.

And regarding your last comment, it IS a bug that on one hand every other case is covered except for method (struct/variable type) binding. On both points. You can see that my code works, it does not present any risk of bugs arising that I most vigorously support Go for keeping as goals.

The workarounds were not difficult, but at the same time, changes in the language spec don't even go into the 2.0 discussion at all. They would break no code, neither integrating slices with interface{}, nor the addition of (in a reasonable, unambiguous place) variable binding to type Name func constructions.

@golang golang locked and limited conversation to collaborators Apr 23, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants