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

Copying field declarations from one type to another(quasi-inheritance) #19383

Closed
underrated opened this issue Nov 22, 2016 · 11 comments
Closed
Labels
domain:types and dispatch Types, subtyping and method dispatch

Comments

@underrated
Copy link

underrated commented Nov 22, 2016

Some composite types have common fields(same type, same name). Wouldn't code be less redundant if we had a way to automatically copy the field declarations from one type to another without copy pasting them? Here's an example:

type Foo
  a::Int64
  b::Int64
end

type Bar
  @import_fields(Foo) # a and b become members of Bar
  c::Fload64
  d::Int64
end

foo_inst = Foo(1,2)
bar_inst = Bar(1,2,3,4)

print(bar_inst.a) # 1
print(bar_inst.b) # 2

I wrote a small macro that does just that and it seems to work pretty well.

macro import_fields(t)                                                                                                                                                 
  tt = eval(t)                                                                                                                                                         
  fields = fieldnames(tt)                                                                                                                                              
  ex = :()                                                                                                                                                             
  for i = 1:length(fields)                                                                                                                                             
    ft = fieldtype(tt, fields[i])                                                                                                                                      
    if i==1                                                                                                                                                            
      ex = :($(fields[i])::$(ft))                                                                                                                                      
    else                                                                                                                                                               
      ex = :($ex ; $(fields[i]) :: $(ft))                                                                                                                              
    end                                                                                                                                                                
  end                                                                                                                                                                  
  return ex                                                                                                                                                            
end       

I think this functionality should be included in the language or in the standard library.

@yuyichao
Copy link
Contributor

Sounds like a candidate for a package.

@StefanKarpinski
Copy link
Sponsor Member

Some way of addressing this issue would be good to have in the standard library, but at the moment it's unclear what the right direction for this is, so for now, I think that @yuyichao is right that this should go in a package so that people can iterate on it independent of the base language.

@KristofferC
Copy link
Sponsor Member

What is wrong with

type Bar
  foo::Foo
  c::Fload64
  d::Int64
end

?

@yuyichao
Copy link
Contributor

Layout, duck typing.

@JeffBezanson
Copy link
Sponsor Member

See also #4935

@underrated
Copy link
Author

underrated commented Nov 22, 2016

@KristofferC There's nothing particularly wrong with that, I just see it as a different use case(type composition).

I aim to inherit in Bar not only the fields of Foo but also the behavior of Foo. For example:

abstract IFoo
type Foo <: IFoo
  a::Int64
  b::Int64
end

function set_a(self::IFoo, a::Int64)
  self.a = a
end

type Bar <: IFoo
  @import_fields(Foo) # a and b become members of Bar
  c::Fload64
  d::Int64
end

bar_inst = Bar(1,2,3,4)

set_a(bar_inst, 7) # I wouldn't be able to reuse set_a on bar_inst if I used type composition. I would have to reimplement set_a for Bar.

I know that in OOP it's recommended to favor composition over inheritance but that doesn't make inheritance completely useless. Inheritance allows you to reuse state and behavior.

I could of course do set_a(bar_inst.foo, 7) but imagine what that would look like if I had many levels of composition like set_a(obj1.obj2.obj3.obj4.foo, 7) - that doesn't look pretty. That's why I want to have inheritance.

EDIT I also found a very good article explaining when to use composition or inheritance in OOP:
https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose

@underrated
Copy link
Author

@JeffBezanson After a first glimpse I notice that "Abstract types with fields" only allow you to inherit fields from abstract types while my solution allows you to inherit fields from any type, even from multiple types. Are there any benefits in being restricted to inherit fields only from abstract types?

@ararslan
Copy link
Member

Currently types can't inherit from other concrete (i.e. non-abstract) types, so copying fields between concrete types without having them inherit from a common abstract type seems iffy.

@underrated
Copy link
Author

@ararslan You can have them inherit from a common abstract type by "attaching" an abstract type to every concrete type :

abstract IFoo
type Foo <: IFoo
  a::Int64
  b::Int64
end

abstract IBar <: IFoo # This way you achieve subtype polymorphism
type Bar <: IBar
  @import_fields(Foo) # inheritance of state
  c::Fload64
  d::Int64
end

This way you are separating two independent aspects:

  • Subtype polymorphism : done by the abstract types
  • Inheritance of state : done by the import_fields macro

Inheritance of behavior happens automatically with the two above.

These are actually the 3 main components that make up inheritance in most OOP languages and which are not very visible to the programmer.

Thus, in Julia(at least in its current form), inheritance is more of a design pattern than a language feature. It's still not clear to me whether it should be one or the other.

It's also not clear to me yet if using import_fields outside of this pattern would generate other complications.

@kshyatt kshyatt added the domain:types and dispatch Types, subtyping and method dispatch label Nov 24, 2016
@ipod825
Copy link

ipod825 commented May 26, 2017

I implemented macros that does exactly what @underrated said and some more stuff.
Check OOPMacro.jl

@KristofferC
Copy link
Sponsor Member

Closing since this feels very much like package territory (#19383 (comment)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:types and dispatch Types, subtyping and method dispatch
Projects
None yet
Development

No branches or pull requests

8 participants