Scala 2.8'de <: <, <% <ve =: = ne anlama geliyor ve nerede belgeleniyor?


201

Predef için API belgelerinde görebiliyorum genel bir işlev türünün (From) => To alt sınıfları olduklarını , ama hepsi bu kadar. Ne? Belki bir yerde belgeler var, ama arama motorları "<: <" gibi "isimleri" çok iyi işlemez, bu yüzden bulamadım.

Takip eden soru: Bu korkak sembolleri / sınıfları ne zaman kullanmalıyım ve neden?


6
Sorunuza en azından kısmen cevap verebilecek ilgili bir soru: stackoverflow.com/questions/2603003/operator-in-scala
Yardena

13
symbolhound.com sizin kod arama arkadaşınız :)
ron

Haskell typeclasses bu operatörlerin işini yapıyor mu? Örnek: compare :: Ord a => a -> a -> Ordering? Bu Scala konseptini Haskell karşı tarafıyla anlamaya çalışıyorum.
Kevin Meredith

Yanıtlar:


217

Bunlara genelleştirilmiş tip kısıtlamaları denir . Bunlar, tür parametreli bir sınıf veya özellik içinde, tür parametrelerinden birini daha da kısıtlamanıza olanak tanır . İşte bir örnek:

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
}

Örtük argüman evidencederleyici tarafından sağlanır, iff Ais String. Bir olarak düşünmek olabilir kanıtıA olan String--Forum argüman kendisi sadece var olduğunu bilerek, önemli değildir. [değiştir: o bir örtük dönüştürme temsil ettiği şey, teknik olarak aslında önemli olan Aiçin Stringaramak mümkün kılan budur, a.lengthve sana derleyici çığlık yok]

Şimdi şöyle kullanabilirsiniz:

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

Ama ben Foobaşka bir şey içeren içeren kullanmayı denerseniz String:

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

Bu hatayı "Int == String" in kanıtı bulamadığı için okuyabilirsiniz ... olması gerektiği gibi! getStringLengthheybetli daha fazla kısıtlama türüne Aolandan Foogenel olarak gerektirir; yani, yalnızca getStringLengthbir Foo[String]. Bu kısıtlama derleme zamanında uygulanır, bu da iyidir!

<:<ve <%<benzer şekilde çalışır, ancak küçük varyasyonlarla:

  • A =:= B A'nın tam olarak B olması gerektiği anlamına gelir
  • A <:< B"A" ifadesi, B'nin bir alt türü olmalıdır ( basit tip kısıtlamasına benzer <:)
  • A <%< B A demek olmalı , muhtemelen örtük dönüşüm yoluyla B görüntülenebilir (basit tür kısıtlamasına benzer <%)

@Retronym tarafından yapılan bu pasaj , bu tür şeylerin nasıl yapıldığı ve genelleştirilmiş tür kısıtlamalarının şimdi nasıl kolaylaştırdığı hakkında iyi bir açıklamadır.

EK

Takip eden sorunuzu cevaplamak için, itiraf ettiğim örnek oldukça sadık ve açıkçası kullanışlı değil. Ancak List.sumInts, bir tamsayı listesi ekleyen bir yöntem gibi bir şey tanımlamak için bunu hayal edin . Bu yöntemin eski bir sistemde çağrılmasına izin vermek istemezsiniz List, sadece a List[Int]. Ancak, Listtür oluşturucu bu kadar kısıtlı olamaz; yine de dize, foos, çubuk ve notların listelerine sahip olmak istersiniz. Bu nedenle, genelleştirilmiş bir tür kısıtlaması yerleştirerek sumInts, yalnızca bu yöntemin yalnızca a üzerinde kullanılabilecek ek bir kısıtlamaya sahip olmasını sağlayabilirsiniz List[Int]. Temelde belirli listeler için özel durum kodu yazıyorsunuz.


3
Tamam, ama aynı isimlerle Manifestbahsetmediğin yöntemler de var .
Daniel C. Sobral

3
Üzerinde yöntemleri Manifestvardır <:<ve >:>OP genelleştirilmiş tip kısıtlamaları tam 3 çeşit söz beri sadece ..., bunu o ilgilenen ne olduğunu varsayıyorum.
Tom Crockett

12
@IttayD: oldukça zekice ... class =:=[From, To] extends From => To, hangi türde bir örtülü değeri demek olduğunu From =:= Toaslında örtük olan dönüşüm gelen Frometmek To. Yani türünde bir örtülü parametresini kabul ederek A =:= Stringbunu söylüyorsun Aörtülü dönüştürülebilir String. Sipariş değişti ve örtük argüman türünde yaptıysanız String =:= Abu bir örtük dönüştürme olacağını, bunun nedeni, işe yaramaz Stringiçin A.
Tom Crockett

25
Bu üç karakterli sembollerin isimleri var mı? Scala'nın sembol çorbasıyla ilgili sorunum, sözlü olarak konuşmak zor olmaları ve kullanımlarını tartışmak ve örneklerini bulmak için Google'ı veya başka bir arama motorunu kullanmak neredeyse imkansız.
Gigatron

4
@Andrea Nope, bu yalnızca türler tam olarak eşitse işe yarayacaktır. From =:= ToKapsamda tür örtük bir değere sahip olmak, örtük bir dönüşüm olduğunu ima ettiğini From => To, ancak ima geri çalışmadığını söyledi; örtük dönüştürme sahip A => Bgelmez değil sen bir örneğini sahip ima A =:= B. =:=içinde tanımlanmış scala.Predefve örtük ve türden yalnızca bir kamuya açık örneği vardır A =:= A. Böylece, örtük bir tür değerinin eşit ve eşit A =:= Bolduğu gerçeğine şahit olacağından emin olabilirsiniz . AB
Tom Crockett

55

Tam bir cevap değil (diğerleri bunu zaten yanıtladı), sadece sözdizimini daha iyi anlamaya yardımcı olan aşağıdakileri not etmek istedim: Örneğin, pelotom örneğinde olduğu gibi, normalde bu "operatörleri" kullanma şekliniz:

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

tip operatörleri için Scala'nın alternatif infix sözdizimini kullanır .

Yani, A =:= Stringile aynıdır =:=[A, String](ve =:=sadece süslü görünümlü bir isim veya özelliktir). Bu sözdiziminin "normal" sınıflarla da çalıştığını unutmayın, örneğin şunları yazabilirsiniz:

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

bunun gibi:

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

Bu yöntem çağrıları ile "normal" için iki sözdiziminden benziyor .ve ()ve operatör sözdizimi.


2
upvote ihtiyacı var, çünkü makes use of Scala's alternative infix syntax for type operators.her şeyin mantıklı olmadığı bu açıklamayı tamamen kaçırıyor
Ovidiu Dolha

39

Bu yapıların ne olduğunu anlamak için diğer cevapları okuyun. İşte bunları ne zaman kullanmalısınız. Bunları yalnızca belirli türler için bir yöntemi kısıtlamanız gerektiğinde kullanırsınız.

İşte bir örnek. Aşağıdaki gibi homojen bir Çift tanımlamak istediğinizi varsayalım:

class Pair[T](val first: T, val second: T)

Şimdi şöyle bir yöntem eklemek istiyorsunuz smaller:

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

Bu sadece Tsipariş edildiğinde çalışır . Sınıfın tamamını kısıtlayabilirsiniz:

class Pair[T <: Ordered[T]](val first: T, val second: T)

Ama bu bir utanç gibi görünüyor - Tsipariş edilmediğinde sınıfın kullanımları olabilir . Bir tür kısıtlamasıyla, yine de smalleryöntemi tanımlayabilirsiniz :

def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

Bu, diyelim ki, örneğini bir sorun yok Pair[File], sürece çağrıyı yok gibi smaller üzerinde.

Durumda Option, uygulayıcılar orNullmantıklı olmasa da bir yöntem istemişlerdir Option[Int]. Bir tür kısıtlaması kullanarak, her şey yolunda. Sen kullanabilirsiniz orNull, bir de Option[String]ve bir oluşturabilirler Option[Int]zamandır demiyorlar gibi, onu ve kullanımı orNullüzerinde. Eğer denerseniz Some(42).orNull, büyüleyici mesajı alırsınız

 error: Cannot prove that Null <:< Int

2
Bunun bu yanıttan yıllar sonra geldiğini anlıyorum, ancak bunun için kullanım örnekleri arıyorum <:<ve bence Orderedörnek artık çok zorlayıcı değil, çünkü artık özellik Orderingyerine dakikayı kullanmayı tercih ediyorsunuz Ordered. Gibi bir şey: def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second.
ebruchez

1
@ebruchez: bir kullanım örneği değiştirilmemiş skaladaki

17

Nerede kullanıldıklarına bağlıdır. Çoğu zaman, örtük parametre türleri bildirilirken kullanıldığında, bunlar sınıflardır. Nadir durumlarda da nesne olabilirler. Son olarak, Manifestnesneler üzerinde operatör olabilirler . Bunlar içinde tanımlanırscala.Predefİlk iki vakada, özellikle iyi belgelenmiş olmasa da, .

Sınıflar arasındaki ilişkiyi test etmek için bir yol sağlamayı amaçlamaktadır, tıpkı <:ve<% ikincisi kullanılamaz zaman durumlarda, yap.

"Onları ne zaman kullanmalıyım?" Sorusuna gelince, bilmeniz gerekmiyor, cevap vermemelisiniz. :-) DÜZENLEME : Tamam, tamam, kütüphaneden bazı örnekler. Açık Either, sizde:

/**
  * 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)
 }

Açık Option, sizde:

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

Koleksiyonlarda başka örnekler de bulacaksınız.


:-)bunlardan bir tane daha? Ve "Onları ne zaman kullanmalıyım?" birçok şey için geçerlidir.
Mike Miller

"Onlar sınıflar arasındaki ilişkiyi test etmek için bir yol sağlamak içindir" <- yardımcı olmak için çok genel
Jeff

3
“Soruya gelince,“ ne zaman kullanmalıyım? ”, Cevap bilmemelisiniz, bilmeniz gerekmiyorsa.” <- Bu yüzden soruyorum. Bu kararlılığı kendim için yapabilmek istiyorum.
Jeff
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.