Julia will be released in version 1.0 on August 6th of this year, and I’m sure many of you who have been on the fence are already jumping in. This series of articles will talk about the Julia language based on some of the problems and experiences I have experienced during the development of Yao. Because it is not PL background, I will not introduce too many details from the language design, everything from the actual use of feelings.

The OO of Julia is completely different from other languages. Julia is OO based on multiple distribution and type systems. The system is also cleverly designed, so some people say that Julia’s performance is largely due to the language design.

polymorphism

Julia doesn’t have a class, but in Julia you can also think of everything as an object, and these objects are instances of some type. A Composite Type of Julia can be declared like this, which is almost the same as C. In Julia, there are Composite Type, Primitive Type, etc. The basic syntax will not be introduced in this article. Please refer to the official document (English).

struct Kitty
    name::String
end
Copy the code

Julia can’t be polymorphic by making a method belong to a class like Python/C++. Because a type is not allowed to have methods other than its own constructor. Julia uses multiple distributions to solve this problem. What is multiple distribution? See my other article: PyTorch source Code Analysis (5)

So in Julia, method means the interaction between types, not the interaction between classes. There have been many discussions on Zhihu about the advantages and disadvantages of traditional OOP. In mathematics, physics and other scientific computing fields, we often need to define a lot of complex relationships. In concept, this way is more direct, OOP is not very suitable in many scientific computing scenarios. Julia’s OO based on multiple distributions and types might be a more appropriate attempt. For example, the addition of a floating point number to an integer

+(lhs::Int32, res::Float64) = # ...
Copy the code

This addition is not an Int and it’s not a Float, so it makes mathematical sense. In general, using Julia as a theoretical object for abstraction is quite natural.

However, if you use Jupyter Notebook, you’ll also see that since methods no longer belong to a class, there is less coupling between methods and types. You can define some methods, run them on the cell, and then define some methods without having to declare all the methods in class.

The type system

Just having multiple distributions provides some polymorphism, but not inheritance-like functionality. This is done by the type system, but please remember not to carry over the ideas inherited from traditional OOP. This is a mistake many Julia beginners, especially those coming from Python/C++, make. Such code is very un-Julian, because the language itself has no concept of inheritance and you’ll find yourself copying code manually and creating a lot of duplicate code. Of course, if you want to write Julia with a style of OOP code, you can do that, but I don’t recommend it.

First, a brief review of the type system. Julia’s Type system is a Type tree made up of abstract types and Concrete types. A child type gets the parent type behavior, but not the parent type members. So Julia is a Duck Type. In the documentation, Julia Team emphasizes that we care more about the behavior of a type than its members, so members cannot be inherited as such.

A lot of people have asked me, what if I have some public member that needs to have all the subtypes? How to make code less repetitive? I will talk about several schemes below, please choose according to your own situation

  1. Public members are static members (not Type traits)

Define the behavior of the share, not the members of the share

abstract type A end struct B <: A end struct C <: A end name(::A) = "no name" # default no name name(::B) = "name B" #Copy the code

2. The members are exactly the same, but the behavior is different

Use Symbol as a label to distribute different behaviors, but they share a parameter type.

struct A{Tag}
    name::String
end

name(x::A{:boy}) = "the boy's name is $(x.name)"
name(x::A{:girl}) = "the girl's name is $(x.name)"
Copy the code

3. Members are different, parts are public, and not static

In this case, we still define the structure of the class by behavior. We need to have a common method interface so that we don’t have to worry about the specific structure of the class. Inevitably, though, you’ll need to encapsulate the public members with a new type, or you’ll need to write them by hand.

struct A
    m1
    m2
    name
end

name(x::A) = x.name

struct B
    m1
    name
end

name(x::B) = x.name
Copy the code

So when we use a type, we don’t encourage calling a type member through. We encourage calling a method, which is the behavior of using a type. But in fact, when you actually implement it, by decoupling it properly, you’ll find that the third case actually happens less often, and more often one or two. Think again if you encounter the third case.

To sum up, behaviour is more important in Julia than anything else. A type is just a label to distribute behavior.