W dokumentach API dla Predef widzę, że są one podklasami ogólnego typu funkcji (From) => To, ale to wszystko, co mówi. Co? Może gdzieś jest dokumentacja, ale wyszukiwarki nie radzą sobie z „nazwami” takimi jak „<: <”, więc nie byłem w stanie jej znaleźć.
Dalsze pytanie: kiedy powinienem używać tych funky symboli / klas i dlaczego?
typeclass
wykonują zadania tych operatorów? Przykładcompare :: Ord a => a -> a -> Ordering
:? Próbuję zrozumieć tę koncepcję Scali w odniesieniu do jej odpowiednika w Haskell.Odpowiedzi:
Są to tak zwane ograniczenia typu uogólnionego . Pozwalają one, w obrębie klasy lub cechy sparametryzowanej na typ, na dalsze ograniczenie jednego z jego parametrów typu. Oto przykład:
Argument niejawny
evidence
jest dostarczany przez kompilator, iffA
jestString
. Można myśleć o nim jako dowód , żeA
jestString
sama --the argumentem nie jest ważne, tylko wiedząc, że ona istnieje. [edytuj: cóż, technicznie jest to faktycznie ważne, ponieważ reprezentuje niejawną konwersję zA
naString
, co pozwala ci dzwonić,a.length
a kompilator nie krzyczy na ciebie]Teraz mogę go używać w następujący sposób:
Ale jeśli spróbuję użyć tego z
Foo
zawartością czegoś innego niżString
:Możesz odczytać ten błąd jako „nie można znaleźć dowodów na to, że Int == String” ... tak powinno być!
getStringLength
nakłada dalsze ograniczenia na rodzaj,A
niżFoo
ogólnie wymaga; mianowicie, możesz wywoływać tylkogetStringLength
naFoo[String]
. To ograniczenie jest wymuszane w czasie kompilacji, co jest fajne!<:<
i<%<
działają podobnie, ale z niewielkimi zmianami:A =:= B
oznacza, że A musi być dokładnie BA <:< B
oznacza, że A musi być podtypem B (analogicznie do ograniczenia typu prostego<:
)A <%< B
oznacza, że A musi być widoczne jako B, prawdopodobnie poprzez niejawną konwersję (analogicznie do prostego ograniczenia typu<%
)Ten fragment kodu @retronym jest dobrym wytłumaczeniem tego, jak kiedyś tego rodzaju rzeczy były osiągane i jak ogólne ograniczenia typów ułatwiają to teraz.
UZUPEŁNIENIE
Aby odpowiedzieć na twoje pytanie uzupełniające, co prawda podany przeze mnie przykład jest dość przemyślany i nie jest oczywiście użyteczny. Ale wyobraź sobie, że używasz go do zdefiniowania czegoś takiego jak
List.sumInts
metoda, która tworzy listę liczb całkowitych. Nie chcesz zezwalać na wywoływanie tej metody na żadnym starymList
, tylkoList[Int]
. JednakList
konstruktor typu nie może być tak constrainted; nadal chcesz mieć listę ciągów znaków, foos, barów i innych rzeczy. Tak więc, umieszczając uogólnione ograniczenie typusumInts
, możesz upewnić się, że tylko ta metoda ma dodatkowe ograniczenie, którego można użyć tylko w przypadkuList[Int]
. Zasadniczo piszesz specjalny kod dla niektórych rodzajów list.źródło
Manifest
, o których nie wspomniałeś.Manifest
są<:<
i>:>
tylko ... ponieważ OP wspomniał dokładnie o 3 odmianach uogólnionych ograniczeń typów, zakładam, że to właśnie był tym zainteresowany.class =:=[From, To] extends From => To
, co oznacza, że domyślna wartość typuFrom =:= To
jest w rzeczywistości niejawną konwersją zFrom
naTo
. Tak więc, akceptując niejawny parametr typu, oA =:= String
którym mówisz, żeA
można go niejawnie przekonwertowaćString
. Jeśli zmienisz kolejność i sprawisz, że domyślny argument będzie typuString =:= A
, nie zadziała, ponieważ byłaby to niejawna konwersja zString
naA
.From =:= To
w zakresie oznacza, że masz niejawną konwersjęFrom => To
, ale implikacja nie działa wstecz; mającą niejawna konwersjaA => B
ma nie sugerować masz instancjęA =:= B
.=:=
jest zapieczętowaną klasą abstrakcyjną zdefiniowaną wscala.Predef
i ma tylko jedną publicznie ujawnioną instancję, która jest niejawna i jest typuA =:= A
. Masz więc gwarancję, że domyślna wartość typuA =:= B
świadczy o tymA
iB
jest równa.Nie jest to kompletna odpowiedź (inni już na to odpowiedzieli), chciałem tylko zwrócić uwagę na następujące, które mogą pomóc lepiej zrozumieć składnię: sposób, w jaki zwykle używasz tych „operatorów”, jak na przykład w przykładzie pelotom:
korzysta z alternatywnej składni poprawki Scali dla operatorów typów .
Tak więc
A =:= String
jest to samo co=:=[A, String]
(i=:=
jest tylko klasą lub cechą o fantazyjnie wyglądającym imieniu). Zauważ, że ta składnia działa również z klasami „normalnymi”, na przykład możesz napisać:lubię to:
Jest podobny do dwóch składni dla wywołania metody, „normalne” z
.
a()
i składni operatora.źródło
makes use of Scala's alternative infix syntax for type operators.
całkowicie brakuje tego wyjaśnienia, bez którego cała sprawa nie ma sensuPrzeczytaj pozostałe odpowiedzi, aby zrozumieć, jakie są te konstrukty. Oto, kiedy powinieneś ich użyć. Używasz ich, gdy musisz ograniczyć metodę tylko do określonych typów.
Oto przykład. Załóżmy, że chcesz zdefiniować jednorodną parę:
Teraz chcesz dodać metodę
smaller
, taką jak ta:Działa to tylko w przypadku
T
zamówienia. Możesz ograniczyć całą klasę:Ale to wydaje się wstydem - klasa może mieć zastosowanie, gdy
T
nie zostanie zamówiona. Z ograniczeniem typu nadal możesz zdefiniowaćsmaller
metodę:Jest ok instancję, powiedzmy,
Pair[File]
, dopóki nie dzwońsmaller
na nim.W przypadku
Option
implementatorów potrzebna byłaorNull
metoda, chociaż nie ma to sensuOption[Int]
. Dzięki zastosowaniu ograniczenia typu wszystko jest w porządku. Możesz użyćorNull
naOption[String]
i możesz utworzyćOption[Int]
i używać, dopóki nie zadzwoniszorNull
. Jeśli spróbujeszSome(42).orNull
, otrzymasz uroczą wiadomośćźródło
<:<
i myślę, że tenOrdered
przykład nie jest już tak przekonujący, ponieważ wolałbyś raczej używaćOrdering
czcionki klasowej niżOrdered
cechy. Coś jak:def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
.To zależy od tego, gdzie są używane. Najczęściej używane podczas deklarowania typów parametrów niejawnych są klasami. W rzadkich przypadkach mogą być obiektami. Wreszcie mogą być operatorami
Manifest
obiektów. Są one zdefiniowanescala.Predef
w pierwszych dwóch przypadkach, choć nie są szczególnie dobrze udokumentowane.Mają one zapewnić sposób przetestowania relacji między klasami, podobnie jak
<:
i<%
zrobić, w sytuacjach, gdy nie można użyć tej drugiej.Jeśli chodzi o pytanie „kiedy powinienem ich użyć?”, Odpowiedź brzmi: nie powinieneś, chyba że wiesz, że powinieneś. :-) EDYCJA : Ok, ok, oto kilka przykładów z biblioteki. W dniu
Either
masz:W dniu
Option
masz:Znajdziesz inne przykłady w kolekcjach.
źródło
:-)
jeden z nich? I zgodziłbym się, że twoja odpowiedź na „Kiedy powinienem ich użyć?” dotyczy bardzo wielu rzeczy.