Modules
Julia'da modüller, kodu tutarlı birimlere organize etmeye yardımcı olur. Söz dizimi olarak module NameOfModule ... end
içinde sınırlıdırlar ve aşağıdaki özelliklere sahiptirler:
- Modüller, her biri yeni bir global kapsam tanıtan ayrı ad alanlarıdır. Bu, aynı ismin farklı işlevler veya global değişkenler için çelişki olmadan kullanılmasına olanak tanıdığı için faydalıdır, yeter ki bunlar ayrı modüllerde olsun.
- Modüller, ayrıntılı ad alanı yönetimi için olanaklar sunar: her biri
export
ettiği vepublic
olarak işaretlediği bir isimler seti tanımlar veusing
veimport
ile diğer modüllerden isimler alabilir (bunları aşağıda açıklıyoruz). - Modüller daha hızlı yükleme için önceden derlenebilir ve çalışma zamanı başlatması için kod içerebilir.
Genellikle, daha büyük Julia paketlerinde modül kodunun dosyalara düzenlendiğini göreceksiniz, örn.
module SomeModule
# export, public, using, import statements are usually here; we discuss these below
include("file1.jl")
include("file2.jl")
end
Dosyalar ve dosya adları genellikle modüllerle ilgisizdir; modüller yalnızca modül ifadeleriyle ilişkilidir. Bir modül başına birden fazla dosya ve bir dosya başına birden fazla modül olabilir. include
, kaynak dosyanın içeriğinin dahil eden modülün global kapsamında değerlendirilmiş gibi davranır. Bu bölümde, kısa ve basit örnekler kullandığımız için include
kullanmayacağız.
Önerilen stil, modülün gövdesini içe doğru kaydırmamaktır, çünkü bu genellikle tüm dosyaların içe kaydırılmasına yol açar. Ayrıca, modül adları için (tıpkı türler gibi) UpperCamelCase
kullanmak yaygındır ve benzer şekilde adlandırılmış bir tanımlayıcı içeriyorsa, ad çakışmalarını önlemek için çoğul formunu kullanmak yaygındır. Örneğin,
module FastThings
struct FastThing
...
end
end
Namespace management
Ad alan yönetimi, bir modüldeki isimlerin diğer modüllerde kullanılabilir hale getirilmesi için dilin sunduğu olanakları ifade eder. İlgili kavramları ve işlevselliği aşağıda ayrıntılı olarak tartışıyoruz.
Qualified names
Fonksiyonlar, değişkenler ve sin
, ARGS
ve UnitRange
gibi türler, her zaman bir modüle, ebeveyn modül denilen bir modüle aittir. Bu modül, örneğin parentmodule
ile etkileşimli olarak bulunabilir.
julia> parentmodule(UnitRange)
Base
Birisi bu isimlere ana modülünün önüne modülünü ekleyerek, örneğin Base.UnitRange
, dışarıdan da atıfta bulunabilir. Bu bir nitelikli isim olarak adlandırılır. Ana modül, Base.Math.sin
gibi bir dizi alt modül aracılığıyla erişilebilir, burada Base.Math
modül yolu olarak adlandırılır. Sözdizimsel belirsizlikler nedeniyle, yalnızca semboller içeren bir ismi nitelendirmek, iki nokta eklemeyi gerektirir; örneğin Base.:+
. Küçük bir sayıdaki operatörler ayrıca parantez gerektirir; örneğin Base.:(==)
.
Eğer bir isim nitelikli ise, o zaman her zaman erişilebilirdir ve bir fonksiyon durumunda, nitelikli ismi fonksiyon adı olarak kullanarak ona yöntemler de eklenebilir.
Bir modül içinde, bir değişken adı global x
olarak ilan edilerek "rezerv" edilebilir, bu da ona bir değer atamadan yapılır. Bu, yükleme zamanından sonra başlatılan küresel değişkenler için isim çakışmalarını önler. M.x = y
sözdizimi, başka bir modülde bir küresel değişkene atama yapmak için çalışmaz; küresel atama her zaman modül yerelidir.
Export lists
İsimler (fonksiyonlar, türler, global değişkenler ve sabitler anlamında) bir modülün ihracat listesine export
ile eklenebilir: bunlar, modülü using
ile kullandığınızda içe aktarılan sembollerdir. Genellikle, kaynak kodunu okuyanların kolayca bulabilmesi için modül tanımının en üstünde veya yakınında yer alırlar, örneğin
julia> module NiceStuff
export nice, DOG
struct Dog end # singleton type, not exported
const DOG = Dog() # named instance, exported
nice(x) = "nice $x" # function, exported
end;
ama bu sadece bir stil önerisi — bir modül, rastgele yerlerde birden fazla export
ifadesine sahip olabilir.
API (uygulama programlama arayüzü) parçası olan isimleri dışa aktarmak yaygındır. Yukarıdaki kodda, dışa aktarma listesi kullanıcıların nice
ve DOG
kullanmalarını önermektedir. Ancak, nitelikli isimler her zaman tanımlayıcıları erişilebilir kıldığından, bu sadece API'leri düzenlemek için bir seçenektir: diğer dillerin aksine, Julia'nın modül iç yapılarını gerçekten gizlemek için olanakları yoktur.
Ayrıca, bazı modüller hiç isim dışa aktarmaz. Bu genellikle, API'lerinde türev
gibi yaygın kelimeler kullandıklarında yapılır; bu, diğer modüllerin dışa aktarma listeleriyle kolayca çakışabilir. Aşağıda isim çakışmalarını nasıl yöneteceğimizi göreceğiz.
Bir ismi kamuya açık olarak işaretlemek için, using NiceStuff
kullananların ad alanına aktarmadan export
yerine public
kullanılabilir. Bu, kamuya açık adı(ları) kamu API'sinin bir parçası olarak işaretler, ancak herhangi bir ad alanı etkisi yoktur. public
anahtar kelimesi yalnızca Julia 1.11 ve üzeri sürümlerde mevcuttur. Julia 1.10 ve alt sürümlerle uyumluluğu korumak için, @compat
makrosunu Compat paketinden kullanın.
Standalone using
and import
Etkileşimli kullanım için, bir modülü yüklemenin en yaygın yolu using ModuleName
'dir. Bu loads ModuleName
ile ilişkili kodu yükler ve getirir.
- modül adı
- ve ihracat listesinin unsurlarını çevresindeki küresel ad alanına.
Teknik olarak, using ModuleName
ifadesi, ModuleName
adında bir modülün ihtiyaç duyulduğunda isimleri çözmek için mevcut olacağı anlamına gelir. Mevcut modülde tanımı olmayan bir global değişkenle karşılaşıldığında, sistem bunu ModuleName
tarafından dışa aktarılan değişkenler arasında arayacak ve orada bulunursa kullanacaktır. Bu, mevcut modül içindeki o global değişkenin tüm kullanımlarının ModuleName
içindeki o değişkenin tanımına karşılık geleceği anlamına gelir.
Bir paketten bir modülü yüklemek için using ModuleName
ifadesi kullanılabilir. Yerel olarak tanımlanmış bir modülden bir modül yüklemek için modül adından önce bir nokta eklenmelidir, örneğin using .ModuleName
.
Devam etmek için örneğimize,
julia> using .NiceStuff
yukarıdaki kodu yükleyecek, NiceStuff
(modül adı), DOG
ve nice
'i kullanılabilir hale getirecektir. Dog
dışa aktarma listesinde yer almaz, ancak adı modül yolu ile nitelendirilirse (burada sadece modül adı) NiceStuff.Dog
olarak erişilebilir.
Önemli olarak, using ModuleName
dışa aktarma listelerinin önemli olduğu tek biçimdir.
Buna karşılık,
julia> import .NiceStuff
sadece modül adını kapsam içine alır. Kullanıcılar içeriğine erişmek için NiceStuff.DOG
, NiceStuff.Dog
ve NiceStuff.nice
kullanmak zorundadır. Genellikle, import ModuleName
, kullanıcının ad alanını temiz tutmak istediği durumlarda kullanılır. Bir sonraki bölümde göreceğimiz gibi import .NiceStuff
, using .NiceStuff: NiceStuff
ile eşdeğerdir.
Birden fazla using
ve import
ifadesini aynı türde, virgülle ayrılmış bir ifadede birleştirebilirsiniz, örneğin:
julia> using LinearAlgebra, Random
using
and import
with specific identifiers, and adding methods
using ModuleName:
veya import ModuleName:
ifadesinden sonra virgülle ayrılmış bir isimler listesi geldiğinde, modül yüklenir, ancak yalnızca o belirli isimler ad alanına bu ifade ile getirilir. Örneğin,
julia> using .NiceStuff: nice, DOG
nice
ve DOG
isimlerini içe aktaracağım.
Önemli olarak, NiceStuff
modül adı isim alanında yer almayacaktır. Eğer erişilebilir olmasını istiyorsanız, onu açıkça listelemeniz gerekir, şöyle:
julia> using .NiceStuff: nice, DOG, NiceStuff
İki veya daha fazla paket/modül bir ismi dışa aktardığında ve bu isim her bir pakette aynı şeye referans vermediğinde, ve paketler using
ile açık bir isim listesi olmadan yüklendiğinde, o ismi nitelik olmadan referans vermek bir hata olur. Bu nedenle, bağımlılıklarının ve Julia'nın gelecekteki sürümleriyle ileriye dönük uyumlu olması amaçlanan kodların, örneğin, yayımlanan paketlerde, her bir yüklü paketten kullandığı isimleri listelemesi önerilir; örneğin, using Foo: Foo, f
yerine using Foo
kullanmak.
Julia'nın iki formu, görünüşte aynı şey için vardır çünkü yalnızca import ModuleName: f
yöntemi, f
'ye modül yolu olmadan yöntem eklemeye izin verir. Yani, aşağıdaki örnek bir hata verecektir:
julia> using .NiceStuff: nice
julia> struct Cat end
julia> nice(::Cat) = "nice 😸"
ERROR: invalid method definition in Main: function NiceStuff.nice must be explicitly imported to be extended
Stacktrace:
[1] top-level scope
@ none:0
[2] top-level scope
@ none:1
Bu hata, yalnızca kullanmayı amaçladığınız diğer modüllerdeki fonksiyonlara yanlışlıkla yöntem eklenmesini engeller.
Bununla başa çıkmanın iki yolu vardır. Fonksiyon adlarını her zaman bir modül yolu ile nitelendirebilirsiniz:
julia> using .NiceStuff
julia> struct Cat end
julia> NiceStuff.nice(::Cat) = "nice 😸"
Alternatif olarak, belirli bir fonksiyon adını import
edebilirsiniz:
julia> import .NiceStuff: nice
julia> struct Cat end
julia> nice(::Cat) = "nice 😸"
nice (generic function with 2 methods)
Hangisini seçeceğiniz bir stil meselesidir. İlk form, başka bir modüldeki bir işleve bir yöntem eklediğinizi açıkça belirtir (unutmayın, ithalatlar ve yöntem tanımı ayrı dosyalarda olabilir), ikinci form ise daha kısadır, bu da birden fazla yöntem tanımlıyorsanız özellikle kullanışlıdır.
Bir değişken using
veya import
ile görünür hale getirildiğinde, bir modül aynı isimle kendi değişkenini oluşturamaz. İçe aktarılan değişkenler salt okunurdur; bir küresel değişkene atama yapmak her zaman mevcut modül tarafından sahip olunan bir değişkeni etkiler veya aksi takdirde bir hata oluşturur.
Renaming with as
Bir import
veya using
ile kapsam içine alınan bir tanımlayıcı, as
anahtar kelimesi ile yeniden adlandırılabilir. Bu, isim çakışmalarını aşmak ve isimleri kısaltmak için yararlıdır. Örneğin, Base
read
fonksiyonunu dışa aktarırken, CSV.jl paketi de CSV.read
sağlar. Eğer CSV okuma işlemini birçok kez gerçekleştireceksek, CSV.
niteliklerini atlamak pratik olacaktır. Ancak bu durumda, Base.read
mi yoksa CSV.read
mi kastettiğimiz belirsiz hale gelir:
julia> read;
julia> import CSV: read
WARNING: ignoring conflicting import of CSV.read into Main
Yeniden adlandırma bir çözüm sunar:
julia> import CSV: read as rd
İçe aktarılan paketler kendileri de yeniden adlandırılabilir:
import BenchmarkTools as BT
as
yalnızca bir tanımlayıcı kapsamına alındığında using
ile çalışır. Örneğin using CSV: read as rd
çalışır, ancak using CSV as C
çalışmaz, çünkü bu, CSV
içindeki tüm dışa aktarılan adlar üzerinde işlem yapar.
Mixing multiple using
and import
statements
Birden fazla using
veya import
ifadesi yukarıdaki formlardan herhangi biri kullanıldığında, etkileri göründükleri sıraya göre birleştirilir. Örneğin,
julia> using .NiceStuff # exported names and the module name
julia> import .NiceStuff: nice # allows adding methods to unqualified functions
NiceStuff
'ın tüm dışa aktarılan isimlerini ve modül adını kapsam içine alacak ve ayrıca nice
'e modül adı ile önek eklemeden yöntemler eklemeye de olanak tanıyacaktır.
Handling name conflicts
Durumu düşünün; iki (veya daha fazla) paket aynı ismi dışa aktarıyorsa, örneğin
julia> module A
export f
f() = 1
end
A
julia> module B
export f
f() = 2
end
B
using .A, .B
ifadesi çalışır, ancak f
'yi çağırmaya çalıştığınızda bir hata alırsınız ve bir ipucu ile karşılaşırsınız.
julia> using .A, .B
julia> f
ERROR: UndefVarError: `f` not defined in `Main`
Hint: It looks like two or more modules export different bindings with this name, resulting in ambiguity. Try explicitly importing it from a particular module, or qualifying the name with the module it should come from.
Burada, Julia hangi f
'yi kastettiğinizi belirleyemiyor, bu yüzden bir seçim yapmalısınız. Aşağıdaki çözümler yaygın olarak kullanılmaktadır:
Basitçe,
A.f
veB.f
gibi nitelikli isimlerle devam edin. Bu, kodunuzun okuyucusuna bağlamı net bir şekilde iletir, özellikle def
farklı paketlerde farklı anlamlara sahip oluyorsa. Örneğin,degree
matematikte, doğal bilimlerde ve günlük hayatta çeşitli kullanımlara sahiptir ve bu anlamların ayrı tutulması gerekir.as
anahtar kelimesini yukarıda bir veya her iki tanımlayıcıyı yeniden adlandırmak için kullanın, örn.```jldoctest module_manual julia> using .A: f as f
julia> using .B: f as g
```
B.f
'yig
olarak kullanılabilir hale getirecektir. Burada,f
'yi ad alanına getirecek olanusing A
ifadesini daha önce kullanmadığınızı varsayıyoruz.Söz konusu isimler anlam paylaştığında, bir modülün diğerinden bunu içe aktarması veya yalnızca bu tür bir arayüz tanımlama işlevine sahip hafif bir "temel" paket bulundurması yaygındır; bu, diğer paketler tarafından kullanılabilir. Bu tür paket isimlerinin
...Base
ile bitmesi gelenekseldir (bu, Julia'nınBase
modülüyle ilgili değildir).
Default top-level definitions and bare modules
Modüller otomatik olarak using Core
, using Base
içerir ve eval
ve include
fonksiyonlarının tanımlarını içerir; bu fonksiyonlar, o modülün küresel kapsamı içinde ifadeleri/dosyaları değerlendirir.
Eğer bu varsayılan tanımlar istenmiyorsa, modüller baremodule
anahtar kelimesi kullanılarak tanımlanabilir (not: Core
hala içe aktarılmaktadır). baremodule
açısından, standart bir module
şöyle görünür:
baremodule Mod
using Base
eval(x) = Core.eval(Mod, x)
include(p) = Base.include(Mod, p)
...
end
Eğer Core
bile istenmiyorsa, hiçbir şey içermeyen ve hiç isim tanımlamayan bir modül Module(:YourNameHere, false, false)
ile tanımlanabilir ve içine kod @eval
veya Core.eval
ile değerlendirilebilir:
julia> arithmetic = Module(:arithmetic, false, false)
Main.arithmetic
julia> @eval arithmetic add(x, y) = $(+)(x, y)
add (generic function with 1 method)
julia> arithmetic.add(12, 13)
25
Standard modules
Üç önemli standart modül vardır:
Core
dilin içine "gömülü" tüm işlevselliği içerir.Base
hemen hemen tüm durumlarda yararlı olan temel işlevselliği içerir.Main
en üst düzey modül ve Julia başlatıldığında mevcut modüldür.
Varsayılan olarak Julia, bazı standart kütüphane modülleri ile birlikte gelir. Bunlar, normal Julia paketleri gibi davranır, ancak bunları açıkça yüklemenize gerek yoktur. Örneğin, bazı birim testleri yapmak istiyorsanız, Test
standart kütüphanesini aşağıdaki gibi yükleyebilirsiniz:
using Test
Submodules and relative paths
Modüller alt modüller içerebilir, aynı module ... end
sözdizimini kullanarak iç içe geçebilirler. Bu, karmaşık kod tabanlarını düzenlemek için yararlı olabilecek ayrı ad alanları tanıtmak için kullanılabilir. Her module
kendi scope'ini tanıttığı için, alt modüller otomatik olarak ebeveynlerinden isim “miras almazlar”.
Alt modüllerin, kapsayıcı ana modül (ve onun) içindeki diğer modüllere göreceli modül nitelikleri kullanarak using
ve import
ifadeleriyle atıfta bulunması önerilir. Göreceli bir modül niteliği, mevcut modülü temsil eden bir nokta (.
) ile başlar ve her bir ardışık .
mevcut modülün üst modülüne işaret eder. Gerekirse modüllerle devam edilmeli ve nihayetinde erişilecek gerçek isim, hepsi .
ile ayrılarak belirtilmelidir.
Aşağıdaki örneği düşünün; burada SubA
alt modülü bir fonksiyon tanımlar ve bu fonksiyon daha sonra "kardeş" modülünde genişletilir:
julia> module ParentModule
module SubA
export add_D # exported interface
const D = 3
add_D(x) = x + D
end
using .SubA # brings `add_D` into the namespace
export add_D # export it from ParentModule too
module SubB
import ..SubA: add_D # relative path for a “sibling” module
struct Infinity end
add_D(x::Infinity) = x
end
end;
Paketlerde kod görebilirsiniz, bu da benzer bir durumda kullanır.
julia> import .ParentModule.SubA: add_D
Ancak bu, code loading üzerinden çalışır ve bu nedenle yalnızca ParentModule
bir paketteyse çalışır. Göreceli yolları kullanmak daha iyidir.
Tanım sıralamasının, değerleri değerlendirirken de önemli olduğunu unutmayın. Dikkate alın.
module TestPackage
export x, y
x = 0
module Sub
using ..TestPackage
z = y # ERROR: UndefVarError: `y` not defined in `Main`
end
y = 1
end
Sub
tanımından önce TestPackage.y
kullanmaya çalışıyor, bu yüzden bir değeri yok.
Benzer nedenlerden dolayı, döngüsel bir sıralama kullanamazsınız:
module A
module B
using ..C # ERROR: UndefVarError: `C` not defined in `Main.A`
end
module C
using ..B
end
end
Module initialization and precompilation
Büyük modüller, bir modüldeki tüm ifadeleri yürütmek genellikle büyük miktarda kod derlemeyi içerdiğinden, yüklenmesi birkaç saniye sürebilir. Julia, bu süreyi azaltmak için modülün önceden derlenmiş önbelleklerini oluşturur.
Önceden derlenmiş modül dosyaları (bazen "önbellek dosyaları" olarak adlandırılır) import
veya using
bir modülü yüklediğinde otomatik olarak oluşturulur ve kullanılır. Eğer önbellek dosyası(ları) henüz mevcut değilse, modül derlenecek ve gelecekteki yeniden kullanım için kaydedilecektir. Ayrıca, modülü yüklemeden bu dosyaları oluşturmak için Base.compilecache(Base.identify_package("modulename"))
komutunu manuel olarak çağırabilirsiniz. Ortaya çıkan önbellek dosyaları DEPOT_PATH[1]
'in compiled
alt klasöründe saklanacaktır. Sisteminizle ilgili hiçbir şey değişmezse, bu tür önbellek dosyaları import
veya using
ile modülü yüklediğinizde kullanılacaktır.
Ön derleme önbellek dosyaları, modüllerin, türlerin, yöntemlerin ve sabitlerin tanımlarını saklar. Ayrıca yöntem özel uzmanlıklarını ve bunlar için üretilen kodu da saklayabilir, ancak bu genellikle geliştiricinin açık precompile
direktifleri eklemesini veya paket derlemesi sırasında derlemeyi zorlayan iş yüklerini çalıştırmasını gerektirir.
Ancak, modülün bağımlılıklarını güncellerseniz veya kaynak kodunu değiştirirseniz, modül using
veya import
ile otomatik olarak yeniden derlenir. Bağımlılıklar, modülün içe aktardığı modüller, Julia derlemesi, dahil ettiği dosyalar veya modül dosyasında include_dependency(path)
ile belirtilen açık bağımlılıklardır.
include
ile yüklenen dosya bağımlılıkları için bir değişiklik, dosya boyutunun (fsize
) veya içeriğin (bir hash'e yoğunlaştırılmış) değişip değişmediğini inceleyerek belirlenir. include_dependency
ile yüklenen dosya bağımlılıkları için bir değişiklik, değişiklik zamanının (mtime
) değişmediğini veya en yakın saniyeye yuvarlanmış değişiklik zamanına eşit olup olmadığını inceleyerek belirlenir (alt saniye hassasiyetiyle mtime'ı kopyalayamayan sistemleri dikkate almak için). Ayrıca, require
içindeki arama mantığı tarafından seçilen dosya yolunun, önceden derleme dosyasını oluşturan yol ile eşleşip eşleşmediğini de dikkate alır. Ayrıca, mevcut işleme zaten yüklenmiş olan bağımlılık setini de dikkate alır ve bu modülleri yeniden derlemeyecek, dosyaları değişse veya kaybolsa bile, çalışan sistem ile önceden derleme önbelleği arasında uyumsuzluk yaratmamak için. Son olarak, herhangi bir compile-time preferences içindeki değişiklikleri de dikkate alır.
Eğer bir modülün önceden derlenmesi için güvenli olmadığını biliyorsanız (örneğin, aşağıda açıklanan nedenlerden biri için), modül dosyasına __precompile__(false)
eklemelisiniz (genellikle en üstte yer alır). Bu, Base.compilecache
'in bir hata fırlatmasına neden olacak ve using
/ import
komutlarının modülü doğrudan mevcut işleme yüklemesini sağlayacak ve önceden derleme ve önbellekleme işlemlerini atlayacaktır. Bu ayrıca, modülün başka bir önceden derlenmiş modül tarafından içe aktarılmasını da engeller.
Belirli davranışların, artımlı paylaşılan kütüphanelerin oluşturulmasında dikkat gerektirebileceğini bilmeniz gerekebilir; bu, modülünüzü yazarken dikkatli olmanızı gerektirebilir. Örneğin, dış durum korunmaz. Bunu accommodate etmek için, çalışma zamanı sırasında gerçekleşmesi gereken herhangi bir başlatma adımını, derleme zamanı sırasında gerçekleşebilecek adımlardan açıkça ayırın. Bu amaçla, Julia, modülünüzde çalışma zamanı sırasında gerçekleşmesi gereken herhangi bir başlatma adımını yürüten bir __init__()
fonksiyonu tanımlamanıza izin verir. Bu fonksiyon, derleme sırasında (--output-*
) çağrılmayacaktır. Etkili bir şekilde, bu fonksiyonun kodun ömrü boyunca tam olarak bir kez çalıştırılacağını varsayabilirsiniz. Gerekirse, elbette manuel olarak çağırabilirsiniz, ancak varsayılan olarak bu fonksiyonun yerel makine için durum hesaplamasıyla ilgilendiğini varsaymak gerekir; bu durum, derlenmiş görüntüde yakalanması gerekmeyen – veya hatta yakalanmaması gereken – bir durumdur. Modül bir işleme yüklendikten sonra çağrılacaktır; bu, artımlı bir derlemeye yükleniyorsa (--output-incremental=yes
) bile geçerlidir, ancak tam bir derleme sürecine yükleniyorsa geçerli değildir.
Özellikle, bir modülde bir function __init__()
tanımlarsanız, Julia modül yüklendikten hemen sonra (örneğin, import
, using
veya require
ile) çalışma zamanında ilk kez __init__()
çağrılacaktır (yani, __init__
yalnızca bir kez çağrılır ve yalnızca modüldeki tüm ifadeler yürütüldükten sonra). Modül tamamen içe aktarıldıktan sonra çağrıldığı için, herhangi bir alt modül veya diğer içe aktarılan modüllerin __init__
fonksiyonları, kapsayıcı modülün __init__
'inden önce çağrılır.
__init__
'in iki tipik kullanımı, dış C kütüphanelerinin çalışma zamanı başlatma işlevlerini çağırmak ve dış kütüphaneler tarafından döndürülen işaretçileri içeren global sabitleri başlatmaktır. Örneğin, çalışma zamanında foo_init()
başlatma işlevini çağırmamızı gerektiren bir C kütüphanesi libfoo
'yu çağırdığımızı varsayalım. Ayrıca, libfoo
tarafından tanımlanan void *foo_data()
işlevinin döndürdüğü değeri tutan bir global sabit foo_data_ptr
tanımlamak istiyoruz - bu sabit, işaretçi adresi her çalıştırmada değişeceği için çalışma zamanında (derleme zamanında değil) başlatılmalıdır. Bunu, modülünüzde aşağıdaki __init__
işlevini tanımlayarak başarabilirsiniz:
const foo_data_ptr = Ref{Ptr{Cvoid}}(0)
function __init__()
ccall((:foo_init, :libfoo), Cvoid, ())
foo_data_ptr[] = ccall((:foo_data, :libfoo), Ptr{Cvoid}, ())
nothing
end
Dikkat edin ki, __init__
gibi bir fonksiyon içinde bir global tanımlamak tamamen mümkündür; bu, dinamik bir dil kullanmanın avantajlarından biridir. Ancak, bunu global kapsamda bir sabit haline getirerek, türün derleyiciye bilindiğini garanti edebilir ve daha iyi optimize edilmiş kod üretmesine izin verebiliriz. Açıkça, foo_data_ptr
'a bağlı olan modülünüzdeki diğer herhangi bir global de __init__
içinde başlatılmalıdır.
ccall
tarafından üretilmeyen çoğu Julia nesnesini içeren sabitlerin __init__
içine yerleştirilmesine gerek yoktur: tanımları önceden derlenebilir ve önbellekli modül görüntüsünden yüklenebilir. Bu, diziler gibi karmaşık yığın tahsisli nesneleri içerir. Ancak, ham bir işaretçi değeri döndüren herhangi bir rutin, ön derlemenin çalışması için çalışma zamanında çağrılmalıdır (Ptr
nesneleri, isbits
nesnesinin içinde gizlenmedikçe null işaretçilerine dönüşecektir). Bu, Julia fonksiyonlarının döndürdüğü değerleri de içerir: @cfunction
ve pointer
.
Sözlük ve küme türleri, ya da genel olarak hash(key)
yönteminin çıktısına bağlı olan herhangi bir şey, daha karmaşık bir durumdur. Anahtarların sayılar, dizeler, semboller, aralıklar, Expr
veya bu türlerin (diziler, demetler, kümeler, çiftler vb. aracılığıyla) bileşimleri olduğu yaygın durumda, önceden derlenmeleri güvenlidir. Ancak, Function
veya DataType
gibi birkaç başka anahtar türü ve hash
yöntemini tanımlamadığınız genel kullanıcı tanımlı türler için, varsayılan hash
yöntemi nesnenin bellek adresine (bu aracılığıyla objectid
) bağlıdır ve bu nedenle her çalıştırmada değişebilir. Bu anahtar türlerinden birine sahipseniz veya emin değilseniz, güvenli olmak için bu sözlüğü __init__
fonksiyonunuzdan başlatabilirsiniz. Alternatif olarak, derleme zamanında güvenli bir şekilde başlatılacak şekilde özel olarak işlenen IdDict
sözlük türünü kullanabilirsiniz.
Önceden derleme kullanırken, derleme aşaması ile yürütme aşaması arasındaki ayrımı net bir şekilde anlamak önemlidir. Bu modda, Julia'nın, derlenmiş kod üreten bağımsız bir yorumlayıcı değil, keyfi Julia kodunun yürütülmesine izin veren bir derleyici olduğu çok daha belirgin hale gelecektir.
Diğer bilinen potansiyel arıza senaryoları şunlardır:
Küresel sayaçlar (örneğin, nesneleri benzersiz bir şekilde tanımlamaya çalışmak için). Aşağıdaki kod parçasını dikkate alın:
julia mutable struct UniquedById myid::Int let counter = 0 UniquedById() = new(counter += 1) end end
bu kodun amacı her örneğe benzersiz bir kimlik vermek olsa da, sayaç değeri derlemenin sonunda kaydedilir. Bu artımlı derlenmiş modülün sonraki tüm kullanımları, o aynı sayaç değerinden başlayacaktır.
objectid
(bellek işaretçisini hashleyerek çalışan) benzer sorunlara sahiptir (aşağıdakiDict
kullanımı notlarına bakın).Bir alternatif,
@__MODULE__
değerini yakalamak için bir makro kullanmak ve bunu mevcutcounter
değeriyle birlikte saklamaktır, ancak bu global duruma bağımlı olmamak için kodu yeniden tasarlamak daha iyi olabilir.Birleşik koleksiyonlar (örneğin
Dict
veSet
)__init__
içinde yeniden hash'lenmelidir. (Gelecekte, bir başlatıcı işlevi kaydetmek için bir mekanizma sağlanabilir.)Derleyim zamanı yan etkilerine bağlı olarak yükleme zamanı boyunca devam eden. Örnekler arasında: diğer Julia modüllerindeki dizileri veya diğer değişkenleri değiştirmek; açık dosyalara veya cihazlara tutamaklar korumak; diğer sistem kaynaklarına (bellek dahil) işaretçiler depolamak;
Küresel durumu başka bir modülden doğrudan referans alarak, arama yolu aracılığıyla değil, "kopyalarını" kazara oluşturma. Örneğin, (küresel kapsamda):
```julia #mystdout = Base.stdout #= will not work correctly, since this will copy Base.stdout into this module =#
instead use accessor functions:
getstdout() = Base.stdout #= best option =#
or move the assignment into the runtime:
init() = global mystdout = Base.stdout #= also works =# ```
Kodun önceden derlenmesi sırasında kullanıcının diğer yanlış davranış durumlarından kaçınmasına yardımcı olmak için yapılabilecek işlemler üzerinde birkaç ek kısıtlama bulunmaktadır:
eval
çağrısı, başka bir modülde yan etki yaratmak için yapılmaktadır. Bu, artan ön derleme bayrağı ayarlandığında bir uyarının da yayımlanmasına neden olacaktır.__init__()
başladıktan sonra yerel kapsamdanglobal const
ifadeleri (bunun için bir hata ekleme planları için bkz. sorun #12010)- Bir modülü değiştirmek, artımlı ön derleme sırasında bir çalışma zamanı hatasıdır.
Dikkate almanız gereken birkaç başka nokta:
- Kaynak dosyalarında yapılan değişikliklerden sonra (
Pkg.update
dahil) hiçbir kod yeniden yüklemesi / önbellek geçersiz kılma işlemi yapılmaz vePkg.rm
sonrasında hiçbir temizlik yapılmaz. - Yeniden şekillendirilmiş bir dizinin bellek paylaşım davranışı ön derleme tarafından göz ardı edilir (her görünüm kendi kopyasını alır).
- Dosya sisteminin derleme zamanı ve çalışma zamanı arasında değişmeden kalmasını beklemek, örneğin
@__FILE__
/source_path()
ile çalışma zamanında kaynakları bulmak veya BinDeps@checked_lib
makrosu. Bazen bu kaçınılmazdır. Ancak, mümkün olduğunda, kaynakları derleme zamanında modüle kopyalamak iyi bir uygulama olabilir, böylece çalışma zamanında bulunmaları gerekmez. WeakRef
nesneleri ve sonlandırıcılar şu anda serileştirici tarafından düzgün bir şekilde işlenmiyor (bu, yaklaşan bir sürümde düzeltilecektir).- Genellikle,
Method
,MethodInstance
,MethodTable
,TypeMapLevel
,TypeMapEntry
gibi içsel meta veri nesnelerinin örneklerine referansları yakalamaktan kaçınmak en iyisidir. Çünkü bu, serileştiriciyi karıştırabilir ve istediğiniz sonuca ulaşmanıza yol açmayabilir. Bunu yapmak zorunda değilsiniz, ancak sistemin bunların bazılarını kopyalamaya çalışacağını ve diğerlerinin tekil bir örneğini oluşturmaya çalışacağını kabul etmeye hazır olmalısınız.
Modül geliştirme sırasında artımlı ön derlemeyi kapatmak bazen faydalı olabilir. Komut satırı bayrağı --compiled-modules={yes|no|existing}
modül ön derlemesini açıp kapatmanıza olanak tanır. Julia --compiled-modules=no
ile başlatıldığında, derleme önbelleğindeki serileştirilmiş modüller, modülleri ve modül bağımlılıklarını yüklerken göz ardı edilir. Bazı durumlarda, mevcut ön derlenmiş modülleri yüklemek isteyebilirsiniz, ancak yenilerini oluşturmak istemezsiniz. Bu, Julia'yı --compiled-modules=existing
ile başlatarak yapılabilir. Daha ayrıntılı kontrol, yalnızca ön derleme sırasında yerel kod depolamasını etkileyen --pkgimages={yes|no|existing}
ile mümkündür. Base.compilecache
hala manuel olarak çağrılabilir. Bu komut satırı bayrağının durumu, paketleri yüklerken, güncellerken ve açıkça inşa ederken otomatik ön derleme tetiklemeyi devre dışı bırakmak için Pkg.build
'e iletilir.
Ayrıca, ortam değişkenleri ile bazı ön derleme hatalarını hata ayıklayabilirsiniz. JULIA_VERBOSE_LINKING=true
ayarını yapmak, derlenmiş yerel kodun paylaşılan kütüphanelerinin bağlantısındaki hataları çözmeye yardımcı olabilir. Julia kılavuzunun Geliştirici Belgeleri bölümüne bakın; burada "Paket Görüntüleri" altında Julia'nın iç yapısını belgeleyen bölümde daha fazla ayrıntı bulacaksınız.