Extract common fields from similar types
djsegal opened this issue · comments
In class you said types weren't DRY enough in situations where various types share some fields.
Could you direct me to a file that exemplifies this concern? This would be useful even if it is just in the phase where you're saying "if I add another similar type I'm going to have to extract some common stuff"
Sure.
It's currently not very much data... but look at u
in Kernel:
https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Kernel.jl
And notice that it has to be copied to the concrete Kernel classes:
https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Diffusion.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Convection.jl
Now... in this simple implementation that's just one data member... but look at all of the data in the real Kernel class in MOOSE:
https://github.com/idaholab/moose/blob/devel/framework/include/kernels/KernelBase.h
And look at all of the hundreds of member variables inherited in the real Diffusion
class in MOOSE through multiple levels of inheritance and from many base classes:
http://mooseframework.org/docs/doxygen/moose/classDiffusion.html
(Scroll down to "Protected Attributes")
If all of those hundreds of member variables had to copy and pasted into every leaf class in MOOSE.jl it would be an absolute disaster...
The workarounds aren't great either. One option is that each of those intermediate base classes could declare a BaseData
type that would be included in the leaf classes. But it would still mean that 23 fields would have to be declared in every leaf class (that's the total number of inherited base classes for Diffusion
). That's a huge amount of duplication (and plenty of room for error)...
Another option is to go to a system where you just pass in a huge DATA
object... and let the leaf functions pull out what they want. This isn't really possible for a number of reasons... the least of which is that it will be incredibly slow (a lot of those data members are being initialized by slow processes at the start of the simulation so that getting the values later is really fast). It's also just not the design I want 😉
Finite elements is just a beast like this. Multiphysics makes it worse. You have a huge amount of data that all needs to trickle down in to (relatively) tiny statements. I mean: look at what the leaf class actually has to implement here:
https://github.com/idaholab/moose/blob/devel/framework/src/kernels/Diffusion.C
That's how I want my Julia code to look too.
Let me know if that makes sense!
(BTW: you can look at any of the inheritance hierarchies in MOOSE.jl and see the same thing. For instance, checkout the DofObject hierarchy:
https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/DofObject.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/Element.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/Node.jl
Both Element
and Node
have to have id
and dofs
. In C++ those would just be in the base class... as they are here: https://github.com/libMesh/libmesh/blob/master/include/base/dof_object.h#L475
)
BTW: I am planning to work around this in MOOSE.jl... I just haven't
decided exactly how I'm going to alter my design yet.
You can see some of this in the fact that the data is already less "flat"
in MOOSE.jl than it is in MOOSE. u
is a Variable
type... and to get
the value and gradient you do u.value
and u.gradient
whereas in MOOSE
those are member variables that you get by doing _u
and _grad_u
.
So: I'm already starting to workaround this... but it can only go so far
before it gets cumbersome. If you have to do
this.that.something.stuff.junk
to get at data... that's not very much
fun. Using inheritance in C++ we can "flatten" the data a lot... to
present the data in a nice way to people extending the objects in MOOSE
(which, are scientists and engineers that frequently would rather not be
creating code at all!).
Just a question before I jump into this. Would I be wrong in assuming that you want a rails-y concern implementation?
something like: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
edit: my bad for getting ahead of myself. no need to respond to this or the next message
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
def self.method_injected_by_foo
...
end
end
end
module Bar
extend ActiveSupport::Concern
include Foo
included do
self.method_injected_by_foo
end
end
class Host
include Bar # It works, now Bar takes care of its dependencies
end
Another example would be from my own codebase for ahab:
module Sluggable
extend ActiveSupport::Concern
included do
include Identifiable
slugged = self.name.constantize::SLUGGED_ON
friendly_id slugged, use: :slugged
validates :slug, uniqueness: { case_sensitive: false }, unless: :is_form_resource?
validates slugged, presence: true
end
end
class User < ApplicationRecord
include Sluggable
...
end
Sorry, just going to close this. My bad.