Julia: Copying field declarations from one type to another(quasi-inheritance)

Created on 22 Nov 2016  ·  10Comments  ·  Source: JuliaLang/julia

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.

types and dispatch

Most helpful comment

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

All 10 comments

Sounds like a candidate for a package.

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.

What is wrong with

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

?

Layout, duck typing.

See also #4935

@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

@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?

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.

@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.

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

Was this page helpful?
0 / 5 - 0 ratings