जूलिया में आवश्यक प्रकार की घोषणा


16

वहाँ किसी भी तरह से करने के लिए स्पष्ट रूप से जूलिया में अपेक्षा करते हैं कि (जैसे एक मॉड्यूल या पैकेज में कहते हैं) है प्रकार चाहिए घोषित किया ? क्या इस तरह के चेक के लिए कोई उदाहरण है PackageCompilerया Lint.jlकोई समर्थन है? अधिक मोटे तौर पर, क्या जूलिया मानक वितरण स्वयं किसी भी स्थिर कोड विश्लेषक या समकक्ष प्रदान करता है जो इस आवश्यकता की जांच करने में मदद कर सकता है?

एक प्रेरक उदाहरण के रूप में, मान लें कि हम यह सुनिश्चित करना चाहते हैं कि हमारा बढ़ता हुआ उत्पादन कोड आधार केवल उस कोड को स्वीकार करता है जो हमेशा टाइप किया जाता है , इस परिकल्पना के तहत कि बड़े कोड प्रकार की घोषणाओं के साथ अधिक रख-रखाव होता है।

यदि हम उस स्थिति को लागू करना चाहते हैं, तो क्या जूलिया अपने मानक वितरण में किसी भी तंत्र को प्रकार की घोषणा की आवश्यकता प्रदान करती है या उस लक्ष्य को आगे बढ़ाने में मदद करती है? (उदाहरण के लिए ऐसा कुछ भी जिसे लिंटर, कमिट हुक या समकक्ष के माध्यम से जांचा जा सकता है?)


1
यह सुनिश्चित नहीं है कि यह कितना मदद करता है, लेकिन, बोगुमिल के विचारों के समान, अगर कोई सामान्य परिभाषित नहीं किया गया है तो hasmethod(f, (Any,) )वापस आ जाएगा false। आपको अभी भी तर्कों की संख्या का मिलान करने की आवश्यकता होगी (हालांकि, hasmethod(f, (Any,Any) )दो-तर्क फ़ंक्शन के लिए)।
तस्सो पापेनिलियनौ

जवाबों:


9

संक्षिप्त उत्तर है: नहीं, वर्तमान में आपके जूलिया कोड की जाँच करने के लिए कोई टूलिंग नहीं है। हालांकि, यह सिद्धांत रूप में संभव है, और अतीत में इस दिशा में कुछ काम किया गया है, लेकिन अभी इसे करने का एक अच्छा तरीका नहीं है।

लंबे समय तक उत्तर यह है कि "टाइप एनोटेशन" यहां एक लाल हेरिंग है, जो आप वास्तव में चाहते हैं वह टाइप चेकिंग है, इसलिए आपके प्रश्न का व्यापक हिस्सा वास्तव में सही प्रश्न है। मैं इस बारे में थोड़ी बात कर सकता हूं कि टाइप एनोटेशन एक लाल हेरिंग क्यों हैं, कुछ अन्य चीजें जो सही समाधान नहीं हैं, और सही प्रकार का समाधान कैसा दिखेगा।

आवश्यक प्रकार के एनोटेशन संभवत: वह नहीं करते हैं जो आप चाहते हैं: ::Anyकोई भी किसी भी क्षेत्र, तर्क या अभिव्यक्ति पर रख सकता है और इसमें एक प्रकार का एनोटेशन होगा, लेकिन ऐसा नहीं है जो आपको या कंपाइलर को उस चीज़ के वास्तविक प्रकार के बारे में उपयोगी कुछ भी बताता है। यह वास्तव में किसी भी जानकारी को जोड़ने के बिना बहुत सारे दृश्य शोर जोड़ता है।

कंक्रीट प्रकार एनोटेशन की आवश्यकता के बारे में क्या? यह नियम ::Anyसब कुछ पर डाल देता है (जो कि जूलिया वैसे भी करता है)। हालांकि, अमूर्त प्रकारों के कई पूरी तरह से वैध उपयोग हैं जो यह अवैध बना देगा। उदाहरण के लिए, identityफ़ंक्शन की परिभाषा है

identity(x) = x

xइस आवश्यकता के तहत आप किस ठोस प्रकार के एनोटेशन पर काम करेंगे ? परिभाषा किसी भी xप्रकार के लिए लागू होती है , प्रकार की परवाह किए बिना - यह फ़ंक्शन के बिंदु की तरह है। एकमात्र प्रकार का एनोटेशन जो सही है x::Any। यह एक विसंगति नहीं है: कई फ़ंक्शन परिभाषाएं हैं जिन्हें सही होने के लिए अमूर्त प्रकार की आवश्यकता होती है, इसलिए ठोस प्रकारों का उपयोग करने के लिए मजबूर करना जूलिया कोड किस तरह का लिख ​​सकता है के संदर्भ में काफी सीमित होगा।

जूलिया में अक्सर "टाइप स्थिरता" की धारणा होती है, जिसके बारे में बात की जाती है। यह शब्द जूलिया समुदाय में उत्पन्न हुआ है, लेकिन अन्य गतिशील भाषा समुदायों द्वारा उठाया गया है, जैसे कि आर। यह परिभाषित करने के लिए थोड़ा मुश्किल है, लेकिन इसका मोटे तौर पर मतलब है कि यदि आप किसी विधि के तर्कों के ठोस प्रकारों को जानते हैं, आप इसके रिटर्न वैल्यू के प्रकार को भी जानते हैं। यहां तक ​​कि अगर कोई विधि स्थिर है, तो यह गारंटी देने के लिए पर्याप्त नहीं है कि वह जांच करेगी क्योंकि प्रकार स्थिरता यह तय करने के लिए किसी भी नियम के बारे में बात नहीं करती है कि कुछ प्रकार की जांच है या नहीं। लेकिन यह सही दिशा में हो रहा है: आप यह जांचना चाहेंगे कि प्रत्येक विधि परिभाषा प्रकार स्थिर है।

आप कई प्रकार की स्थिरता की आवश्यकता नहीं करना चाहते हैं, भले ही आप कर सकें। जूलिया 1.0 के बाद से, छोटे यूनियनों का उपयोग करना आम हो गया है। यह पुनरावृति प्रोटोकॉल के रीडिज़ाइन के साथ शुरू हुआ था, जो अब nothingयह इंगित करने के लिए उपयोग करता है कि पुनरावृति बनाम किया जाता है (value, state)जब पुनरावृति करने के लिए अधिक मान होते हैं, तो एक ट्यूपल लौटाता है । find*मानक पुस्तकालय में कार्य भी की वापसी मान का उपयोग nothingकि कोई मूल्य नहीं पाया गया है इंगित करने के लिए। ये तकनीकी रूप से अस्थिरता हैं, लेकिन वे जानबूझकर हैं और संकलक अस्थिरता के आसपास अनुकूलन करने के बारे में तर्क देने में काफी अच्छे हैं। इसलिए कोड में कम से कम छोटे यूनियनों को अनुमति दी जानी चाहिए। इसके अलावा, लाइन खींचने के लिए कोई स्पष्ट जगह नहीं है। हालांकि शायद कोई कह सकता है कि वापसी का प्रकारUnion{Nothing, T} स्वीकार्य है, लेकिन इससे अधिक अप्रत्याशित कुछ भी नहीं है।

आप वास्तव में क्या चाहते हैं, हालांकि, टाइप एनोटेशन या टाइप स्थिरता की आवश्यकता के बजाय, एक उपकरण है जो यह जांच करेगा कि आपका कोड विधि त्रुटियों को नहीं फेंक सकता है, या शायद अधिक मोटे तौर पर कि यह किसी भी प्रकार की अप्रत्याशित त्रुटि नहीं करेगा। कंपाइलर अक्सर सटीक रूप से निर्धारित कर सकता है कि प्रत्येक कॉल साइट पर किस विधि को बुलाया जाएगा, या कम से कम इसे कुछ तरीकों से संकीर्ण कर देगा। इस प्रकार यह तेज कोड उत्पन्न करता है- पूर्ण गतिशील प्रेषण बहुत धीमा है (उदाहरण के लिए, C ++ में vtables की तुलना में बहुत धीमा)। यदि आपने गलत कोड लिखा है, तो दूसरी ओर, कंपाइलर बिना शर्त त्रुटि का उत्सर्जन कर सकता है: कंपाइलर जानता है कि आपने गलती की है, लेकिन आपको रनटाइम तक नहीं बताता है क्योंकि वे भाषा शब्दार्थ हैं। प्रत्येक व्यक्ति को यह निर्धारित करने की आवश्यकता हो सकती है कि कंपाइलर यह निर्धारित करने में सक्षम हो कि प्रत्येक कॉल साइट पर कौन से तरीके कहे जा सकते हैं: यह गारंटी देता है कि कोड तेज होगा और कोई विधि त्रुटियां नहीं हैं। यही जूलिया के लिए एक अच्छा प्रकार का जाँच उपकरण है। इस तरह की चीज़ के लिए एक शानदार आधार है क्योंकि कंपाइलर पहले से ही कोड को जनरेट करने की प्रक्रिया के हिस्से के रूप में इस काम का ज्यादातर हिस्सा करता है।


12

यह एक दिलचस्प सवाल है। प्रमुख प्रश्न यह है कि हम घोषित प्रकार के रूप में क्या परिभाषित करते हैं । यदि आपका मतलब है कि ::SomeTypeहर विधि की परिभाषा में एक कथन है तो यह करना थोड़ा मुश्किल है क्योंकि आपके पास जूलिया में गतिशील कोड पीढ़ी की अलग-अलग संभावनाएं हैं। शायद इस अर्थ में एक पूर्ण समाधान है, लेकिन मैं इसे नहीं जानता (मैं इसे सीखना पसंद करूंगा)।

हालांकि मेरे दिमाग में जो बात आती है, वह यह है कि ऐसा करने के लिए अपेक्षाकृत सरल प्रतीत होता है कि क्या किसी मॉड्यूल के भीतर परिभाषित कोई विधि Anyइसके तर्क के रूप में स्वीकार करती है। यह समान है लेकिन पहले के बयान के समान नहीं है:

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

methodsफ़ंक्शन के लिए समान रूप में दोनों कार्यों के हस्ताक्षर के xरूप में स्वीकार करता है Any

अब यह जांचने के लिए कि क्या मॉड्यूल / पैकेज में कोई भी विधि Anyइसे परिभाषित करने के किसी भी तरीके के तर्क के रूप में स्वीकार करती है, जैसे कि निम्नलिखित कोड का उपयोग किया जा सकता है (मैंने इसे बड़े पैमाने पर परीक्षण नहीं किया है जैसा कि मैंने अभी इसे लिखा है, लेकिन यह ज्यादातर लगता है कवर संभव मामले):

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

अब जब आप इसे Base.Iteratorsमॉड्यूल पर चलाते हैं तो आपको मिलता है:

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

और जब आप उदाहरण के लिए DataStructures.jl पैकेज की जाँच करें:

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

मैं जो प्रस्ताव करता हूं वह आपके प्रश्न का पूर्ण समाधान नहीं है लेकिन मैंने इसे अपने लिए उपयोगी पाया है इसलिए मैंने इसे साझा करने के बारे में सोचा।

संपादित करें

उपरोक्त कोड केवल fहोना स्वीकार करता है Function। सामान्य तौर पर आपके पास ऐसे प्रकार हो सकते हैं जो कॉल करने योग्य हो सकते हैं। तब check_declared(m::Module, f::Function)हस्ताक्षर को बदल दिया जा सकता है check_declared(m::Module, f)(वास्तव में तब फ़ंक्शन खुद को Anyदूसरे तर्क के रूप में अनुमति देगा :)) और इस फ़ंक्शन के सभी मूल्यांकित नामों को पास करेगा। तब आपको जांचना होगा कि क्या फ़ंक्शन के अंदर methods(f)सकारात्मक lengthहै ( methodsगैर-कॉल करने योग्य रिटर्न के लिए एक मूल्य है जिसकी लंबाई है 0)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.