Julia'da tür bildirimi gerekli


16

Julia'da türlerin bildirilmesi gerektiğini açıkça belirtmenin bir yolu var mı (örneğin, bir modül veya paket içinde diyelim) ? Örneğin , bu tür kontroller için destek var mı veya herhangi bir destek var mı ? Daha genel olarak, Julia standart dağıtımının kendisi bu gereksinimi kontrol etmeye yardımcı olabilecek herhangi bir statik kod analizörü veya eşdeğeri sağlıyor mu?PackageCompilerLint.jl

Motive edici bir örnek olarak, büyüyen üretim kodu tabanımızın , tür bildirimleri olan büyük kod tabanlarının daha sürdürülebilir olma eğilimi olduğu hipotezi altında, yalnızca her zaman bildirilen kodu kabul ettiğinden emin olmak istediğimizi varsayalım.

Bu koşulu uygulamak istiyorsak, Julia standart dağıtımında tip beyanı gerektiren veya bu amacın ilerletilmesine yardımcı olacak herhangi bir mekanizma sağlıyor mu? (örneğin, tüyler, işlem kancaları veya eşdeğeri aracılığıyla kontrol edilebilecek bir şey var mı?)


1
bunun ne kadar yardımcı olduğuna emin değilim, ancak Bogumil'in düşüncelerine benzer şekilde, herhangi bir jenerik tanımlanmamışsa hasmethod(f, (Any,) )geri dönecektir false. Yine de argüman sayısını eşleştirmeniz gerekir (örneğin hasmethod(f, (Any,Any) ), iki argüman işlevi için).
Tasos Papastylianou

Yanıtlar:


9

Kısa cevap: hayır, şu anda Julia kodunuzu kontrol etmek için bir araç yok. Bununla birlikte, prensip olarak mümkündür ve geçmişte bu yönde bazı çalışmalar yapılmıştır, ancak şu anda bunu yapmanın iyi bir yolu yoktur.

Uzun cevap, "tip ek açıklamaları" burada kırmızı bir ringa balığıdır, gerçekten istediğiniz şey tip kontrolüdür, bu nedenle sorunuzun daha geniş kısmı aslında doğru sorudur. Tip ek açıklamalarının neden kırmızı bir ringa balığı olduğu, doğru çözüm olmayan bazı şeyler ve doğru çözümün nasıl görüneceği hakkında biraz konuşabilirim.

Tür ek açıklamaları istemek büyük olasılıkla istediğinizi gerçekleştiremez: biri ::Anyherhangi bir alana, bağımsız değişkene veya ifadeye koyabilir ve bir tür ek açıklamasına sahip olabilir, ancak size veya derleyiciye o şeyin gerçek türü hakkında yararlı bir şey söyleyen bir ek açıklama içermez . Herhangi bir bilgi eklemeden çok fazla görsel parazit ekler.

Somut tip ek açıklamalar talep etmeye ne dersiniz? Bu sadece ::Anyher şeyi (Julia'nın dolaylı olarak yaptığı şey) koymaktır. Bununla birlikte, bunun yasa dışı hale getireceği soyut türlerin mükemmel şekilde geçerli birçok kullanımı vardır. Örneğin, identityişlevin tanımı

identity(x) = x

xBu gereksinim altında hangi somut tip ek açıklamalarını koyabilirsiniz ? Tanım x, türden bağımsız olarak herhangi biri için geçerlidir; bu, işlevin noktasıdır. Doğru olan tek tür ek açıklamasıdır x::Any. Bu bir anomali değildir: doğru olması için soyut türler gerektiren birçok işlev tanımı vardır, bu yüzden bunları beton türlerini kullanmaya zorlamak, ne tür Julia kodu yazabileceği açısından oldukça sınırlayıcı olacaktır.

Julia'da sıkça konuşulan bir “tip istikrarı” nosyonu var. Terimin Julia topluluğundan kaynaklandığı anlaşılıyor, ancak R gibi diğer dinamik dil toplulukları tarafından seçildi. Tanımlamak biraz zor, ancak kabaca bir yöntemin argümanlarının somut türlerini biliyorsanız, dönüş değerinin türünü de biliyorsunuz. Bir yöntem tür kararlı olsa bile, bu tür bir denetim yazacağını garanti etmek için yeterli değildir, çünkü tür kararlılığı, bir türün kontrol edip etmediğine karar vermek için herhangi bir kural hakkında konuşmaz. Ancak bu doğru yöne doğru ilerliyor: her yöntem tanımının tip kararlı olup olmadığını kontrol etmek istersiniz.

Birçoğunuz, yapabilseniz bile, tip kararlılığına ihtiyaç duymak istemezsiniz. Julia 1.0'dan beri küçük sendikaların kullanımı yaygınlaştı. Bu, yinelemenin daha fazla değer olduğunda nothingbir (value, state)demet döndürmek yerine yinelemenin yapıldığını belirtmek için kullanılan yineleme protokolünün yeniden tasarımı ile başladı . find*Standart kütüphanede fonksiyonlar da bir geri dönüş değeri kullanmak nothinghiçbir değer bulunmuştur belirtmek için. Bunlar teknik olarak kararsızlıklardır, ancak kasıtlıdırlar ve derleyici kararsızlık etrafında optimizasyon yapma konusunda oldukça iyidir. Bu nedenle, en azından küçük sendikaların kodda olmasına izin verilmelidir. Üstelik çizgiyi çizmek için net bir yer yok. Her ne kadar belki bir dönüş türü söylenebilirUnion{Nothing, T} kabul edilebilir, ancak bundan daha öngörülemez bir şey değil.

Bununla birlikte, muhtemelen gerçekten istediğiniz şey, tür ek açıklamaları veya tür kararlılığı gerektirmek yerine, kodunuzun yöntem hataları alamayacağını veya daha geniş bir şekilde beklenmedik bir hata atmayacağını kontrol edecek bir araca sahip olmaktır. Derleyici genellikle her çağrı sitesinde hangi yöntemin çağrılacağını kesin olarak belirleyebilir veya en azından birkaç yönteme daraltabilir. Bu şekilde hızlı kod üretir; tam dinamik gönderim çok yavaştır (örneğin, C ++ 'daki araçlardan çok daha yavaştır). Yanlış kod yazdıysanız, derleyici koşulsuz bir hata verebilir: derleyici bir hata yaptığınızı bilir, ancak bunlar dil anlambilimi olduğundan çalışma zamanına kadar size söylemez. Derleyicinin her çağrı sitesinde hangi yöntemlerin çağrılabileceğini belirlemesi gerekebilir: bu, kodun hızlı olacağını ve hiçbir yöntem hatası olmadığını garanti eder. Julia için iyi bir tip kontrol aracı bunu yapmalı. Bu tür bir şey için büyük bir temel var, çünkü derleyici zaten bu işin çoğunu kod oluşturma sürecinin bir parçası olarak yapıyor.


12

Bu ilginç bir soru. Anahtar soru, beyan edilen tür olarak tanımladığımız şeydir . ::SomeTypeHer yöntem tanımında bir ifade varsa, Julia'da farklı dinamik kod oluşturma olasılıklarına sahip olduğunuz için yapmak biraz zor. Belki bu anlamda tam bir çözüm var ama bilmiyorum (öğrenmek isterim).

Aklıma gelen, göreceli olarak daha basit görünen şey, bir modülde tanımlanan herhangi bir yöntemin Anyargüman olarak kabul edilip edilmediğini kontrol etmektir . Bu benzerdir, ancak önceki ifadeye eşdeğer değildir:

julia> z1(x::Any) = 1
z1 (generic function with 1 method)

julia> z2(x) = 1
z2 (generic function with 1 method)

julia> methods(z1)
# 1 method for generic function "z1":
[1] z1(x) in Main at REPL[1]:1

julia> methods(z2)
# 1 method for generic function "z2":
[1] z2(x) in Main at REPL[2]:1

Aynı bakmak methodshem fonksiyonların imza kabul olarak fonksiyonu xolarak Any.

Şimdi bir modül / paketteki Anyherhangi bir yöntem aşağıdaki kod kullanılabilir gibi bir şey içinde tanımlanan yöntemlerin herhangi bir argüman olarak kabul edip etmediğini kontrol etmek için (Ben sadece yazdım gibi kapsamlı test etmedim, ama çoğunlukla gibi görünüyor olası durumları kapsamalıdır):

function check_declared(m::Module, f::Function)
    for mf in methods(f).ms
        if mf.module == m
            if mf.sig isa UnionAll
                b = mf.sig.body
            else
                b = mf.sig
            end
            x = getfield(b, 3)
            for i in 2:length(x)
                if x[i] == Any
                    println(mf)
                    break
                end
            end
        end
    end
end

function check_declared(m::Module)
    for n in names(m)
        try
            f = m.eval(n)
            if f isa Function
                check_declared(m, f)
            end
        catch
            # modules sometimes return names that cannot be evaluated in their scope
        end
    end
end

Şimdi Base.Iteratorsmodül üzerinde çalıştırdığınızda şunları elde edersiniz:

julia> check_declared(Iterators)
cycle(xs) in Base.Iterators at iterators.jl:672
drop(xs, n::Integer) in Base.Iterators at iterators.jl:628
enumerate(iter) in Base.Iterators at iterators.jl:133
flatten(itr) in Base.Iterators at iterators.jl:869
repeated(x) in Base.Iterators at iterators.jl:694
repeated(x, n::Integer) in Base.Iterators at iterators.jl:714
rest(itr::Base.Iterators.Rest, state) in Base.Iterators at iterators.jl:465
rest(itr) in Base.Iterators at iterators.jl:466
rest(itr, state) in Base.Iterators at iterators.jl:464
take(xs, n::Integer) in Base.Iterators at iterators.jl:572

ve örneğin DataStructures.jl paketini kontrol ettiğinizde şunları elde edersiniz:

julia> check_declared(DataStructures)
compare(c::DataStructures.LessThan, x, y) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\heaps.jl:66
compare(c::DataStructures.GreaterThan, x, y) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\heaps.jl:67
cons(h, t::LinkedList{T}) where T in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\list.jl:13
dec!(ct::Accumulator, x, a::Number) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\accumulator.jl:86
dequeue!(pq::PriorityQueue, key) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\priorityqueue.jl:288
dequeue_pair!(pq::PriorityQueue, key) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\priorityqueue.jl:328
enqueue!(s::Queue, x) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\queue.jl:28
findkey(t::DataStructures.BalancedTree23, k) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\balanced_tree.jl:277
findkey(m::SortedDict, k_) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\sorted_dict.jl:245
findkey(m::SortedSet, k_) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\sorted_set.jl:91
heappush!(xs::AbstractArray, x) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\heaps\arrays_as_heaps.jl:71
heappush!(xs::AbstractArray, x, o::Base.Order.Ordering) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\heaps\arrays_as_heaps.jl:71
inc!(ct::Accumulator, x, a::Number) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\accumulator.jl:68
incdec!(ft::FenwickTree{T}, left::Integer, right::Integer, val) where T in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\fenwick.jl:64
nil(T) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\list.jl:15
nlargest(acc::Accumulator, n) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\accumulator.jl:161
nsmallest(acc::Accumulator, n) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\accumulator.jl:175
reset!(ct::Accumulator{#s14,V} where #s14, x) where V in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\accumulator.jl:131
searchequalrange(m::SortedMultiDict, k_) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\sorted_multi_dict.jl:226
searchsortedafter(m::Union{SortedDict, SortedMultiDict, SortedSet}, k_) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\tokens2.jl:154
sizehint!(d::RobinDict, newsz) in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\robin_dict.jl:231
update!(h::MutableBinaryHeap{T,Comp} where Comp, i::Int64, v) where T in DataStructures at D:\AppData\.julia\packages\DataStructures\iymwN\src\heaps\mutable_binary_heap.jl:250

Önerdiğim soruya tam bir çözüm değil ama kendim için yararlı buldum, bu yüzden paylaşmayı düşündüm.

DÜZENLE

Yukarıdaki kod sadece kabul fedilir Function. Genel olarak çağrılabilir türlere sahip olabilirsiniz. Daha sonra check_declared(m::Module, f::Function)imza olarak değiştirilebilir check_declared(m::Module, f)(aslında fonksiyonun kendisi Anyikinci argüman olarak izin verir :)) ve değerlendirilen tüm isimleri bu fonksiyona geçirebilir. Daha sonra , işlevin içinde methods(f)pozitif olup olmadığını kontrol etmeniz gerekir length( methodsçağrılamaz olan gibi uzunluklu bir değer döndürür 0).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.