Czy jest jakikolwiek sposób na jawne wymaganie w Julii (np. Powiedzenie w module lub pakiecie), że typy muszą być deklarowane ? Czy na przykład PackageCompiler
czy Lint.jl
ma żadnego wsparcia dla takich kontroli? Mówiąc szerzej, czy sama standardowa dystrybucja Julii zapewnia jakiś statyczny analizator kodu lub równoważny, który mógłby pomóc w sprawdzeniu tego wymogu?
Jako motywujący przykład, powiedzmy, że chcemy się upewnić, że nasza rosnąca baza kodu produkcyjnego akceptuje tylko kod, który zawsze jest deklarowany typu , zgodnie z hipotezą, że duże bazy kodu z deklaracjami typów są łatwiejsze w utrzymaniu.
Jeśli chcemy egzekwować ten warunek, czy Julia w swojej standardowej dystrybucji zapewnia mechanizmy wymagające deklaracji typu lub pomaga osiągnąć ten cel? (np. cokolwiek, co można sprawdzić za pomocą włókien, haczyków zatwierdzających lub równoważnych?)
źródło
hasmethod(f, (Any,) )
powróci,false
jeśli nie zdefiniowano żadnego generycznego. Nadal będziesz musiał dopasować liczbę argumentów (tj.hasmethod(f, (Any,Any) )
Dla funkcji dwuargumentowej).Odpowiedzi:
Krótka odpowiedź brzmi: nie, obecnie nie ma narzędzi do sprawdzania typu kodu Julia. Zasadniczo jest to jednak możliwe i w przeszłości wykonano pewne prace w tym kierunku, ale obecnie nie ma na to dobrego sposobu.
Dłuższą odpowiedzią jest to, że „adnotacje typu” są tutaj czerwonym śledziem, tak naprawdę chcesz sprawdzania typu, więc szersza część pytania jest właściwie właściwym pytaniem. Mogę trochę porozmawiać o tym, dlaczego adnotacje tekstowe są czerwonym śledziem, o innych rzeczach, które nie są właściwym rozwiązaniem, i jak wyglądałoby właściwe rozwiązanie.
Wymaganie adnotacji typu prawdopodobnie nie pozwala osiągnąć zamierzonego celu: można po prostu wstawić
::Any
dowolne pole, argument lub wyrażenie i mieć adnotację typu, ale nie taką, która mówi tobie lub kompilatorowi coś użytecznego na temat rzeczywistego typu tej rzeczy. Dodaje dużo szumu wizualnego bez dodawania jakichkolwiek informacji.Co z wymaganiem konkretnych adnotacji typu? To wyklucza po prostu zakładanie
::Any
wszystkiego (co i tak Julia domyślnie robi). Istnieje jednak wiele doskonale uzasadnionych zastosowań typów abstrakcyjnych, które spowodowałyby niezgodność z prawem. Na przykład definicjaidentity
funkcji toJaką adnotację na konkretny typ zastosowałbyś w
x
ramach tego wymogu? Definicja ma zastosowanie do każdegox
, niezależnie od typu - taki jest punkt funkcji. Jedyną poprawną adnotacją typu jestx::Any
. To nie jest anomalia: istnieje wiele definicji funkcji, które wymagają poprawnych typów abstrakcyjnych, więc zmuszanie ich do używania konkretnych typów byłoby dość ograniczające pod względem rodzaju kodu Julii, który można napisać.W Julii często mówi się o „stabilności typu”. Termin wydaje się pochodzić ze społeczności Julii, ale został wybrany przez inne dynamiczne społeczności językowe, takie jak R. Jest to trochę trudne do zdefiniowania, ale z grubsza oznacza, że jeśli znasz konkretne typy argumentów metody, znasz również typ jego wartości zwracanej. Nawet jeśli metoda jest stabilna typu, to nie wystarczy, aby zagwarantować, że sprawdziłaby typ, ponieważ stabilność typu nie mówi o żadnych regułach decydujących o tym, czy coś ma sprawdzić. Ale zmierza to we właściwym kierunku: chcesz mieć możliwość sprawdzenia, czy każda definicja metody jest stabilna pod względem typu.
Wielu z was nie chce wymagać stabilności tekstu, nawet gdybyście mogli. Od Julii 1.0 powszechne jest używanie małych związków. Rozpoczęło się to od przeprojektowania protokołu iteracji, który teraz używa
nothing
do wskazania, że iteracja została wykonana, zamiast zwracania(value, state)
krotki, gdy istnieje więcej wartości do iteracji. Tefind*
funkcje biblioteki standardowej również używać wartości zwracanejnothing
, aby wskazać, że żadna wartość nie została odnaleziona. Są to technicznie niestabilności typu, ale są one celowe, a kompilator jest całkiem dobry w uzasadnianiu ich optymalizacji na podstawie niestabilności. Więc przynajmniej małe związki prawdopodobnie muszą być dozwolone w kodzie. Ponadto nie ma wyraźnego miejsca na narysowanie linii. Chociaż może można powiedzieć, że typ zwrotuUnion{Nothing, T}
jest do zaakceptowania, ale nic bardziej nieprzewidywalnego niż to.Tym, czego prawdopodobnie naprawdę nie chcesz, zamiast wymagać adnotacji lub stabilności typu, jest posiadanie narzędzia, które sprawdzi, czy Twój kod nie może generować błędów metod, a może szerzej, że nie spowoduje żadnego nieoczekiwanego błędu. Kompilator często może dokładnie określić, która metoda zostanie wywołana w każdej witrynie wywołującej, lub przynajmniej zawęzić ją do kilku metod. W ten sposób generuje szybki kod - pełna dynamiczna wysyłka jest bardzo powolna (na przykład znacznie wolniejsza niż vtables w C ++). Z drugiej strony, jeśli napisałeś niepoprawny kod, kompilator może emitować bezwarunkowy błąd: kompilator wie, że popełniłeś błąd, ale nie mówi ci do czasu wykonania, ponieważ są to semantyka języka. Można wymagać, aby kompilator mógł określić, które metody mogą być wywoływane w każdej witrynie wywoływania: to gwarantuje, że kod będzie szybki i że nie będzie żadnych błędów metod. To powinno zrobić dobre narzędzie do sprawdzania typów dla Julii. Jest to świetny fundament dla tego rodzaju rzeczy, ponieważ kompilator wykonuje już większość tej pracy w ramach procesu generowania kodu.
źródło
To interesujące pytanie. Kluczowym pytaniem jest to, co definiujemy jako deklarowany typ . Jeśli masz na myśli, że
::SomeType
w każdej definicji metody znajduje się instrukcja, jest to nieco trudne, ponieważ masz różne możliwości dynamicznego generowania kodu w Julii. Być może istnieje kompletne rozwiązanie w tym sensie, ale nie wiem (chciałbym się tego nauczyć).Rzecz, która przychodzi mi do głowy, wydaje się stosunkowo prostsza do wykonania, to sprawdzenie, czy jakakolwiek metoda zdefiniowana w module akceptuje
Any
jako argument. Jest to podobne, ale nie równoważne z wcześniejszym stwierdzeniem, ponieważ:wyglądają tak samo dla
methods
funkcji, jak podpis obu funkcji przyjmujex
jakoAny
.Teraz, aby sprawdzić, czy jakakolwiek metoda w module / pakiecie akceptuje
Any
jako argument dowolnej z zdefiniowanych w niej metod, można użyć czegoś takiego jak następujący kod (nie testowałem go dokładnie, ponieważ właśnie go zapisałem, ale wydaje się, że jest to głównie możliwe przypadki):Teraz, gdy uruchomisz go na
Base.Iterators
module, otrzymasz:a gdy np. sprawdzisz pakiet DataStructures.jl, otrzymasz:
To, co proponuję, nie jest pełnym rozwiązaniem twojego pytania, ale uznałem je za przydatne dla siebie, więc pomyślałem o podzieleniu się nim.
EDYTOWAĆ
Powyższy kod zgadza
f
sięFunction
tylko. Zasadniczo możesz mieć typy, które można wywoływać. Następniecheck_declared(m::Module, f::Function)
podpis mógłby zostać zmieniony nacheck_declared(m::Module, f)
(właściwie wtedy sama funkcja pozwoliłabyAny
jako drugi argument :)) i przekazałaby wszystkie ocenione nazwy do tej funkcji. Następnie musisz sprawdzić, czy w funkcjimethods(f)
jest dodatnilength
(tak jak wmethods
przypadku braku wywołania zwraca wartość o długości0
).źródło