Co znaczą<:<, < % < i=: = w Scali 2.8 i gdzie są one udokumentowane?

Widzę w API docs for Predef, że są to podklasy 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 "jak"<:>

Kolejne pytanie: Kiedy powinienem używać tych funky symboli/klas i dlaczego?

Author: Jeff, 2010-08-07

4 answers

Nazywa się je uogólnionymi ograniczeniami typu . Pozwalają one, z poziomu parametryzowanej klasy lub cechy, na dalsze ograniczanie jednego z jej parametrów typu. Oto przykład:

case class Foo[A](a:A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    def getStringLength(implicit evidence: A =:= String) = a.length
}

Implicit argument evidence jest dostarczany przez kompilator, iff A jest String. Możesz myśleć o tym jako dowód , że A jest String--sam argument nie jest ważny, tylko wiedząc, że istnieje. [[34]} [edit: cóż, technicznie rzecz biorąc jest to ważne ponieważ reprezentuje niejawną konwersję z A do String, co pozwala wywołać a.length, a nie mieć kompilatora krzyczącego na Ciebie]

Teraz mogę go używać tak:

scala> Foo("blah").getStringLength
res6: Int = 4

Ale gdybym spróbował użyć go z Foo zawierającym coś innego niż String:

scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]

Można odczytać ten błąd jako "could not find evidence that Int = = String"... tak powinno być! getStringLength nakłada dalsze ograniczenia na rodzaj A niż to, co Foo w ogólne wymagania; mianowicie, można wywołać tylko {[13] } na Foo[String]. To ograniczenie jest egzekwowane w czasie kompilacji, co jest fajne!

<:< i <%< działają podobnie, ale z niewielkimi zmianami:

  • A =:= B oznacza, że A musi być dokładnie B
  • A <:< B oznacza, że A musi być podtypem B (analogicznie do prostej ograniczenia typu <:)
  • A <%< B oznacza, że A musi być viewable jako B, ewentualnie poprzez konwersję implicit (analogiczną do prostego typu constraint <%)

Ten fragment autorstwa @retronim jest dobrym wyjaśnieniem tego, jak tego rodzaju rzeczy były realizowane i jak uogólnione ograniczenia typów ułatwiają to teraz.

Dodatek

Aby odpowiedzieć na twoje kolejne pytanie, przyznam, że przykład, który podałem, jest dość wymyślny i nie jest oczywiście użyteczny. Ale wyobraź sobie, że używasz go do zdefiniowania czegoś w rodzaju metody List.sumInts, która dodaje listę liczb całkowitych. Nie chcesz, aby ta metoda być wywoływane na dowolnym starym List, tylko List[Int]. Jednak konstruktor typu List nie może być tak ograniczony; nadal chcesz mieć listę ciągów znaków, stopek, pasków i innych rzeczy. Tak więc, umieszczając uogólnione ograniczenie typu na sumInts, możesz upewnić się, że tylko ta metoda ma dodatkowe ograniczenie, że może być używana tylko na List[Int]. Zasadniczo piszesz specjalny kod dla niektórych rodzajów list.

 189
Author: Tom Crockett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-08 09:43:05

Nie kompletna odpowiedź( inni już na to odpowiedzieli), chciałem tylko zwrócić uwagę na następujące kwestie, co może pomóc lepiej zrozumieć składnię: sposób, w jaki zwykle używasz tych "operatorów", jak na przykład w przykładzie pelotoma:

def getStringLength(implicit evidence: A =:= String)

Używa alternatywnej składni infiksu Scali dla operatorów typu.

Więc A =:= String jest tym samym co =:=[A, String] (a =:= jest tylko klasą lub cechą o wyszukanej nazwie). Zauważ, że składnia ta działa również z" zwykłymi " klasami, dla przykład można napisać:

val a: Tuple2[Int, String] = (1, "one")

Tak:

val a: Int Tuple2 String = (1, "one")

Jest podobny do dwóch składni wywołań metod, "normalnej" z . i () oraz składni operatora.

 50
Author: Jesper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-07 06:03:34

Przeczytaj Pozostałe odpowiedzi, aby zrozumieć, czym są te konstrukcje. Oto kiedy powinieneś ich użyć. Używasz ich, gdy musisz ograniczyć metodę tylko dla określonych typów.

Oto przykład. Załóżmy, że chcesz zdefiniować jednorodną parę, w ten sposób:
class Pair[T](val first: T, val second: T)

Teraz chcesz dodać metodę smaller, Jak to:

def smaller = if (first < second) first else second

To działa tylko wtedy, gdy T jest zamówione. Można ograniczyć całą klasę:

class Pair[T <: Ordered[T]](val first: T, val second: T)
[[19]}ale to wydaje się wstyd -- tam może być używany dla klasy, gdy T nie jest uporządkowana. Z ograniczeniem typu można jeszcze zdefiniować metodę smaller:
def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

It ' s ok to instantiate, say, a Pair[File], dopóki nie zadzwonisz smaller robi się.

W przypadku Option, implementatorzy chcieli metody orNull, mimo że nie ma to sensu dla Option[Int]. Używając ograniczenia typu, wszystko jest dobrze. Możesz użyć {[12] } na Option[String], i możesz utworzyć Option[Int] i używać go, dopóki nie wywołasz orNull na to. Jeśli spróbujesz Some(42).orNull, otrzymasz uroczą wiadomość

 error: Cannot prove that Null <:< Int
 36
Author: cayhorstmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-13 21:21:19

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ą też być obiektami. Wreszcie, mogą być operatorami na obiektach Manifest. Są one zdefiniowane wewnątrz scala.Predef w dwóch pierwszych przypadkach, choć nie są szczególnie dobrze udokumentowane.

Mają one zapewnić sposób na sprawdzenie relacji między klasami, tak jak robią to <: i <%, w sytuacjach, gdy ta ostatnia nie może być używany.

Co do pytania " Kiedy powinienem ich używać?", odpowiedź brzmi: nie powinieneś, chyba że wiesz, że powinieneś. :- ) EDIT: Ok, ok, oto kilka przykładów z biblioteki. Na Either, masz:

/**
  * Joins an <code>Either</code> through <code>Right</code>.
  */
 def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
   case Left(a)  => Left(a)
   case Right(b) => b
 }

 /**
  * Joins an <code>Either</code> through <code>Left</code>.
  */
 def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
   case Left(a)  => a
   case Right(b) => Right(b)
 }

Na Option, masz:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

Znajdziesz kilka innych przykładów na kolekcjach.

 16
Author: Daniel C. Sobral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-07 16:16:15