Groups 160 of 99+ julia-users › "Namespaces" for accessing fields of composite types 7 posts by 4 authors lars klein Sep 1 I'm currently revisiting Julia. The language seems almost to good to be true. One thing that irks me is the lack of OOP. Some time ago, I read up on all the information regarding this design choice. I don't want to debate it. Apparently that was a very informed and conscious decision, to keep the language lean and fast. However, while coding in Julia, I thought about this. type Cat age::Int end type Tiger innerCat::Cat end In a "classical" OOP language, you would derive tiger from cat and say that it's an "is-a" relationship. In Julia, you have to use composition instead. Assuming you have an instance of type tiger, accessing the age of the tiger becomes this. tiger.innerCat.age What about making the innerCat transparent ? Like this: tiger.age Is that feasible ? Since Julia is a compiled language, it seems to me like this would be a simple job for the compiler. I realize that there might be name-clashes in existing code. But that could be prevented by implementing the rule: The field age is searched top-down in the composite structure. I don't know the implications of this design change. And I don't want to presume. Did you think about this ? Did you decide not to include it, due to a specific reason ? Is the feature more complex than I realize ? If this is possible, the next thing to consider would be adapting multiple dispatch, too. function meow(c::Cat) # age wasn't such a good field choice after all println(age) end Maybe you could introduce the rule that meow(tiger) is implicitly converted to meow(tiger.cat) Once again. I know how it can be annoying to read the contributions of know-it-alls that actually have no clue what is going on. If you say "this is impossible", or even just "we can't do it in a nice way", I have complete understanding. But right now I'm rather enthusiastic about these changes. The second change should be possible via the compiler, too ? Right now you have generic functions. I think that this already implies lots of searching for adequate types. Searching in hierarchies of types should be possible ? The two changes would effectively make Julia a first-class OOP language. You could add a cat object to a tiger and the tiger would behave just like a cat. To my mind, this would be a great improvement in syntax. While keeping the semantics easy to understand. Yichao Yu Sep 1 - show quoted text - The first change will be complicated. Changing the meaning of `.` is not as simple as just chaining the name space. Function call is a known complicated thing and it'll be very bad to make each field as expensive as a function call. The second change is basically impossible. It's agreed that the namespace should be improved and there should be some changes on this before 1.0. Simply allowing overloading `.` or changing the meaning of it in a way that makes it much more expensive is unlikely the way to go (although we might have something with similar function). Michael Borregaard Sep 1 To do something like this in Julia, there are several possibilities, depending what you want to achieve. You could abstract Cat # we decide hat cat will always have an age variable type Tiger <: Cat age::Int end meow(c::Cat) = println(c.age) # then you can tigre = Tiger(5) meow(tigre) lars klein Sep 3 For a toy example like this, this is reasonable advice. But what if the cat has a huge amount of members and many types derive from cat ? You would have to copy and paste all the common data between all the types. That's a problem, especially if something needs to be refactored. Plus: There's no type safety. It seems to me, as if the functions impose some kind of implicit interface on the types they operate on. How does meow know that there exists a field like age ? It seems to me that this could lead to very hard to find bugs. Assuming I have a collection of cat-derivatives. Tigers, lions, panthers,... Each of them should be able to meow. But if I have a typo in one of the fields, I will never notice it, until the method is used with the faulty type. Is it possible (on the roadmap maybe) to add explicit interfaces to the language ? But I think, if there are interfaces which specify fields, the implications are far-reaching. Specifying that a type fulfills an interface is equivalent to specifying the fields. Then why not have the fields added automatically. This addresses the first and second issue. Copy and paste is avoided, as well as bugs due to missing fields. But then an interface would be just like an abstract type with fields. Which is apparently impossible. Could you maybe give me some more feedback on this ? What are the limits of interfaces. Maybe you could have a julia preprocessor that populates the types with fields ? Although I assume that this is undesirable since it makes valid Julia code (valid for the actual compiler) somewhat of a second class citizen. Is it possible to use macros to address these issues ? I'm afraid I've never used them. They seem incredibly powerful (@simd, @parallel). Thanks for the great information so far. Chris Rackauckas Sep 3 In any case where you would do copy-paste programming, you could just use the @def macro from here, and then you just place the code you would want to paste around as @def nameit begin # Code end and then you can paste that code at (compile time) using @nameit. If you want have the same fields everywhere, you can put those fields all in an @def to paste them around with @cat_sharedfields. Or you could define all of the types within a macro, looping throw a dictionary of shared fields to place them in before the non-shared fields. Checkout Base or RMath for ideas on how to do this: in Base there are many cases where macros are used to define many related functions at the same time. So yes, macros are what you're looking for. - show quoted text - Chris Rackauckas Sep 3 You can build this more generically as well. For example, if you make BigCat<:HouseCat, then put anything which extends HouseCat via composition, then you can use type Tiger <: BigCat hcat::HouseCat #Inheritance by composition end meow(c::BigCat) = println(c.hcat.age)#Define a method to make it seamless This will work for every BigCat. You can then define a macro to make a bunch of dispatches like this. - show quoted text - Michael Borregaard Sep 5 > For a toy example like this, this is reasonable advice. I know you were not after advice, but wanted to suggest a design change to Julia. I thought Yichao Yu gave a nice response to that, and I am looking forward to see the developments he promises. I just thought I would point out an ideomatic julian way of acheiving what it it is you want. Generally, of course, I can do no better than proposing a suggestion to solving the example you give. If your use case is different or more encompassing, you could try to describe it and I am sure the mailing list can give you some nice advice. I don't see the issue with typos and copy-pasting. In your example, the normal approach would be to define a type CatData with all the fields and variables you feel a Cat needs, and then design all cats to contain this object.