# 程式風格指引 (Style Guide)

## 避免撰寫過度專一的型別

``convert(Complex{Float64}, x)``

``complex(float(x))``

``````addone(x::Int) = x + 1                 # works only for Int
addone(x::Integer) = x + one(x)    # any integer type
addone(x::Number) = x + one(x)     # any numeric type
addone(x) = x + one(x)             # any type supporting + and oneunit``````

## 呼叫方處理超出範圍的參數

``````function foo(x, y)
x = Int(x); y = Int(y)
...
end
foo(x, y)``````

``````function foo(x::Int, y::Int)
...
end
foo(Int(x), Int(y))``````

## 如果函式會修改到參數的值，在函式名稱之後加上`!`

``````function double(a::AbstractArray{<:Number})
for i = 1:endof(a)
a[i] *= 2
end
return a
end``````

``````function double!(a::AbstractArray{<:Number})
for i = 1:endof(a)
a[i] *= 2
end
return a
end``````

Julia標準程式庫普遍使用這樣的慣例，標準程式庫中也包含了拷貝形式跟修改形式的函式範例(e.g., `sort()``sort!()`)，還有一些只有修改形式(e.g., `push!()`, `pop!()`, `splice!()`)。為了方便，這樣的函式回傳修改過的陣列，也是很典型的例子。

## 避免在field中使用`Union`型別

``````mutable struct MyType
...
x::Union{Void,T}
end``````

• 尋求一個安全的預設值來初始化`x`
• 引進另一個不需要`x`的型別
• 如果有很多跟`x`相似的field，將他們存在一個字典中
• 決定一個簡單的規則來確認`x`是不是`nothing`。舉例來說，通常一個field一開始會是`nothing` 但會在一個明確的地方做初始化。在那個例子中，是讓他一開始維持未定義。
• 如果一個`x`真的需要在某些時候不被賦值，定義他為`::Nullable{T}`，當程式存取這個field時，符合了型別穩定性（見Nullable types）。

## 避免詳盡描述容器型別

``a = Array{Union{Int,AbstractString,Tuple,Array}}(n)``

## 別將條件式括弧起來

Julia不需要在`if``while`中將條件括弧起來。請寫：

``if a == b``

``if (a == b)``

## 不要使用不必要的靜態參數

``foo(x::T) where {T<:Real} = ...``

``foo(x::Real) = ...``

## 避免混淆實例與型別

``````foo(::Type{MyType}) = ...
foo(::MyType) = foo(MyType)``````

Decide whether the concept in question will be written as `MyType` or `MyType()`, and stick to it.

The preferred style is to use instances by default, and only add methods involving `Type{MyType}` later if they become necessary to solve some problem.

If a type is effectively an enumeration, it should be defined as a single (ideally immutable struct or primitive) type, with the enumeration values being instances of it. Constructors and conversions can check whether values are valid. This design is preferred over making the enumeration an abstract type, with the "values" as subtypes.

## 不要過度使用macros

Be aware of when a macro could really be a function instead.

Calling `eval()` inside a macro is a particularly dangerous warning sign; it means the macro will only work when called at the top level. If such a macro is written as a function instead, it will naturally have access to the run-time values it needs.

## 不要在介面上暴露不安全的操作

If you have a type that uses a native pointer:

``````mutable struct NativeType
p::Ptr{UInt8}
...
end``````

don't write definitions like the following:

``getindex(x::NativeType, i) = unsafe_load(x.p, i)``

The problem is that users of this type can write `x[i]` without realizing that the operation is unsafe, and then be susceptible to memory bugs.

Such a function should either check the operation to ensure it is safe, or have `unsafe` somewhere in its name to alert callers.

## 不要重載基本容器型別的方法

``show(io::IO, v::Vector{MyType}) = ...``

## 避免type piracy

"Type piracy"指的是實際延伸或是重新定義Base或是其他套件中的方法到你尚未定義的型別上。某些狀況下，你可以免除type piracy，只有少數不良效應。然而，在極端的狀況，你甚至會使Julia崩潰(e.g. 如果你的延伸或是重新定義導致無法輸入並傳給`ccall`)。 Type piracy會使程式碼的推論惡化（reasoning about code），也可能引進不相容，這些是難以預測跟診斷的。

``````module A
import Base.*
*(x::Symbol, y::Symbol) = Symbol(x,y)
end``````

The problem is that now any other module that uses `Base.*` will also see this definition. Since `Symbol` is defined in Base and is used by other modules, this can change the behavior of unrelated code unexpectedly. There are several alternatives here, including using a different function name, or wrapping the `Symbol`s in another type that you define.

Sometimes, coupled packages may engage in type piracy to separate features from definitions, especially when the packages were designed by collaborating authors, and when the definitions are reusable. For example, one package might provide some types useful for working with colors; another package could define methods for those types that enable conversions between color spaces. Another example might be a package that acts as a thin wrapper for some C code, which another package might then pirate to implement a higher-level, Julia-friendly API.

## Avoid using floats for numeric literals in generic code when possible

If you write generic code which handles numbers, and which can be expected to run with many different numeric type arguments, try using literals of a numeric type that will affect the arguments as little as possible through promotion.

For example,

``````julia> f(x) = 2.0 * x
f (generic function with 1 method)

julia> f(1//2)
1.0

julia> f(1/2)
1.0

julia> f(1)
2.0``````

while

``````julia> g(x) = 2 * x
g (generic function with 1 method)

julia> g(1//2)
1//1

julia> g(1/2)
1.0

julia> g(1)
2``````

As you can see, the second version, where we used an `Int` literal, preserved the type of the input argument, while the first didn't. This is because e.g. `promote_type(Int, Float64) == Float64`, and promotion happens with the multiplication. Similarly, `Rational` literals are less type disruptive than `Float64` literals, but more disruptive than `Int`s:

``````julia> h(x) = 2//1 * x
h (generic function with 1 method)

julia> h(1//2)
1//1

julia> h(1/2)
1.0

julia> h(1)
2//1``````

Thus, use `Int` literals when possible, with `Rational{Int}` for literal non-integer numbers, in order to make it easier to use your code.