Scala'da örtük olarak anlamak


308

Scala playframework dersinden geçiyordum ve beni şaşırtan kod parçacığına rastladım:

def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
        errors => BadRequest(views.html.index(Task.all(), errors)),
        label => {
          Task.create(label)
          Redirect(routes.Application.tasks())
        } 
  )
}

Bu yüzden araştırmaya karar verdim ve bu yazıyla karşılaştım .

Hala anlamıyorum.

Bunun arasındaki fark nedir:

implicit def double2Int(d : Double) : Int = d.toInt

ve

def double2IntNonImplicit(d : Double) : Int = d.toInt

farklı yöntem adlarına sahip oldukları açıktır.

Ne zaman kullanmalıyım implicitve neden?


Bu öğretici gerçekten yararlı buldum
Adrian Moisa

Yanıtlar:


391

Aşağıda imaların ana kullanım durumlarını açıklayacağım, ancak daha fazla ayrıntı için Scala'da Programlama'nın ilgili bölümüne bakın .

Örtük parametreler

Bir yöntemdeki son parametre listesi işaretlenebilir implicit, yani değerler çağrıldıkları bağlamdan alınır. Kapsamda doğru türün örtük değeri yoksa, derlenmez. Örtük değerin tek bir değere çözümlenmesi ve çakışmalardan kaçınması gerektiğinden, türü amacına özel yapmak iyi bir fikirdir, örn. Örtük bulma yöntemlerinize gerek yokturInt !

misal:

  // probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

  // then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")  // returns "***abc"

Örtük dönüşümler

Derleyici, bağlam için yanlış türde bir ifade bulduğunda Function, yazım denetimine izin verecek bir türün örtük değerini arayacaktır. Dolayısıyla A, a gerekliyse ve a bulursa B, B => Akapsamda örtük türde bir değer arar (varsa Bve Atamamlayıcı nesneler gibi bazı diğer yerleri de denetler ). Yana defs "eta-genişletilmiş" içine olabilir Functionnesneler, bir implicit def xyz(arg: B): Ade yapacağız.

Bu nedenle yöntemleriniz arasındaki fark, işaretli olanın implicitderleyici tarafından a Doublebulunduğunda ancak bir Intgerekli olduğunda eklenmesidir .

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

ile aynı şekilde çalışacak

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

İkincisinde dönüştürmeyi manuel olarak ekledik; ilk olarak derleyici otomatik olarak aynısını yaptı. Sol taraftaki tür ek açıklaması nedeniyle dönüşüm gereklidir.


Google Play'deki ilk snippet'inizle ilgili:

Eylemler bu sayfada Play belgelerinden açıklanmaktadır (ayrıca API belgelerine de bakınız ). Kullanıyorsun

apply(block: (Request[AnyContent])Result): Action[AnyContent]

üzerinde Action (aynı isimli özellik ile tamamlayıcı olan) bir nesne.

Dolayısıyla, argüman olarak formda değişmez olarak yazılabilen bir İşlev sağlamamız gerekir

request => ...

Bir fonksiyon değişmezinde, önündeki bölüm =>bir değer bildirimidir ve implicitisterseniz diğer valbildirimlerde olduğu gibi işaretlenebilir . Burada, kontrol yazmak için bunun için işaretlenmesine request gerek yokturimplicit , ancak bunu yaparak fonksiyon içinde ihtiyaç duyabilecek herhangi bir yöntem için örtük bir değer olarak mevcut olacaktır (ve tabii ki açık olarak da kullanılabilir) . Bu özel durumda, Form sınıfı bindFromRequestyönteminin örtük bir bağımsız değişken gerektirdiği için bu yapılmıştır .Request


12
Cevap için teşekkürler. Bölüm 21 için bağlantı gerçekten harika. Teşekkür ederim.
Clive

14
Sadece bunu eklemek için, aşağıdaki video sonuçları ve scala youtube.com/watch?v=IobLWVuD-CQ
Shakti

Yukarıdaki videoda 24 : 25'e atla (55 dakika boyunca dinlemek istemeyenler için)
papigee

36

UYARI: iğneleyici bir şekilde alaycı içerir! YMMV ...

Luigi'nin cevabı tam ve doğrudur. Bu bir sen aşırı gloriously nasıl bir örnekle bunu biraz uzatmak için sadece implicits o Scala projelerinde oldukça sık olduğu gibi,. Aslında sık sık, "En İyi Uygulama" kılavuzlarından birinde bile bulabilirsiniz .

object HelloWorld {
  case class Text(content: String)
  case class Prefix(text: String)

  implicit def String2Text(content: String)(implicit prefix: Prefix) = {
    Text(prefix.text + " " + content)
  }

  def printText(text: Text): Unit = {
    println(text.content)
  }

  def main(args: Array[String]): Unit = {
    printText("World!")
  }

  // Best to hide this line somewhere below a pile of completely unrelated code.
  // Better yet, import its package from another distant place.
  implicit val prefixLOL = Prefix("Hello")
}

1
Haha. İyi mizah.
Det

1
Mizahı takdir ediyorum. Bu tür şeyler, yıllar önce Scala öğrenmeye çalışmamın nedenlerinden biri ve sadece şimdi geri dönüyorum. Asla bazı (birçok) ben bakıyordum kodu gelen emin değildi.
melston

7

requestParametreyi neden ve ne zaman işaretlemelisiniz implicit:

Eyleminizin gövdesinde kullanacağınız bazı yöntemlerin örtük bir parametre listesi vardır , örneğin Form.scala bir yöntem tanımlar:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

Bunu sadece arayacağınız gibi fark myForm.bindFromRequest()etmeniz gerekmez. Örtülü argümanları açıkça belirtmeniz gerekmez. Hayır, derleyiciyi , isteğin bir örneğini gerektiren bir yöntem çağrısıyla karşılaştığında geçerli herhangi bir aday nesneyi aramak üzere terk edersiniz . Eğer bu yana yapmak bir istek yoktur, yapmanız gereken tüm olarak işaretlemek etmektir implicit.

Sen açıkça hazır olarak işaretlemek örtülü kullanım.

Derleyiciye Play çerçevesi tarafından gönderilen istek nesnesini kullanmanın "Tamam" olduğunu ("istek" adını verdik, ancak gerektiğinde "sinsi" olarak "r" veya "req" kullanılmış olabilir) .

myForm.bindFromRequest()

bak? orada değil, ama bir orada!

Sadece gerekli olan her yere manuel olarak yerleştirmek zorunda kalmadan gerçekleşir (ancak işaretli olsun ya da olmasın , açık bir şekilde geçirebilirsiniz implicit):

myForm.bindFromRequest()(request)

Örtük olarak işaretleyerek olmadan, olur zorunda yukarıdaki yapmak. Bunu örtük olarak işaretlemek zorunda değilsiniz.

Ne zaman siz isteği işaretlemelisiniz implicit? Sadece İstek'in bir örneğini bekleyen örtük bir parametre listesi bildiren yöntemleri kullanıyorsanız gerçekten yapmanız gerekir . Ancak basit tutmak için, isteği implicit her zaman işaretleme alışkanlığına girebilirsiniz . Bu şekilde güzel kısa kod yazabilirsiniz.


2
"Bu şekilde güzel kısa kod yazabilirsiniz." Ya da, @DanielDinnyes'in belirttiği gibi, güzelce gizlenmiş kod. Bir örtük maddenin nereden geldiğini izlemek gerçek bir acı olabilir ve dikkatli değilseniz kodun okunmasını ve bakımını zorlaştırabilirler.
melston

7

Scala örtük olarak şöyle çalışır :

Dönüştürücü

Parametre değer enjektörü

Örtük kullanımın 3 türü vardır

  1. Dolaylı olarak dönüşüm yazın : Hata üreten atamayı amaçlanan türe dönüştürür

    val x: String = "1"

    val y: Int = x

Dize Int'nin alt türü değildir , bu nedenle satır 2'de hata oluşur. Hatayı gidermek için derleyici, örtük anahtar sözcüğü olan ve bir String'i argüman olarak alan ve bir Int döndüren kapsamdaki bir yöntemi arar .

yani

implicit def z(a:String):Int = 2

val x :String = "1"

val y:Int = x // compiler will use z here like val y:Int=z(x)

println(y) // result 2  & no error!
  1. Örtük olarak alıcı dönüşümü : Biz genellikle alıcı çağrı nesnesinin özelliklerini, örn. yöntemler veya değişkenler. Bu nedenle, herhangi bir özelliği bir alıcı tarafından çağırmak için, söz konusu alıcının sınıfının / nesnesinin üyesi olması gerekir.

    class Mahadi{
    
    val haveCar:String ="BMW"
    
    }

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi



   mahadi.haveTv // Error happening

Burada mahadi.haveTv bir hata üretir. Çünkü scala derleyicisi öncelikle mahadi alıcısına haveTv özelliğini arayacaktır . Bulamayacak. İkincisi o sahip kapsamında bir yöntem arayacaktır örtülü anahtar kelime almak Mahadi nesnesini argüman olarak ve döner Johnny nesnesi . Ama burada yok. Böylece hata yaratacaktır . Ama aşağıdakiler tamam.

class Mahadi{

val haveCar:String ="BMW"

}

class Johnny{

val haveTv:String = "Sony"

}

val mahadi = new Mahadi

implicit def z(a:Mahadi):Johnny = new Johnny

mahadi.haveTv // compiler will use z here like new Johnny().haveTv

println(mahadi.haveTv)// result Sony & no error
  1. Örtük olarak parametre enjeksiyonu : Bir yöntemi çağırır ve parametre değerini geçmezsek, hataya neden olur. Skala derleyici şu şekilde çalışır - önce değeri geçmeye çalışır, ancak parametre için doğrudan bir değer elde etmez.

    def x(a:Int)= a
    
    x // ERROR happening

İkincisi, parametrenin herhangi bir örtük anahtar sözcüğü varsa , kapsamda aynı değer türüne sahip herhangi bir val arar . Aksi takdirde hataya neden olur.

def x(implicit a:Int)= a

x // error happening here

Bu sorun derleyici bir arayacaktır slove için örtülü val sahip Int türünü parametre çünkü bir sahip örtülü anahtar kelime .

def x(implicit a:Int)=a

implicit val z:Int =10

x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.

Başka bir örnek:

def l(implicit b:Int)

def x(implicit a:Int)= l(a)

ayrıca şöyle yazabiliriz-

def x(implicit a:Int)= l

Çünkü L bir sahiptir örtülü bir parametre ve kapsamındaki yöntem olup x vücudunun bir orada kapalı yerel değişken ( parametreler yerel değişkenler ) bir parametre olan , x , böylece de, X gövdesi yöntemi yöntemi imza L'in örtülü bağımsız değişken değeri olan x yönteminin yerel örtük değişkeni (parametresi) tarafından dolaylı olarak dosyalandıa .

Yani

 def x(implicit a:Int)= l

böyle derleyicide olacak

def x(implicit a:Int)= l(a)

Başka bir örnek:

def c(implicit k:Int):String = k.toString

def x(a:Int => String):String =a

x{
x => c
}

çünkü, hataya neden olur C de x {x => C} açıkça değer geçirme bağımsız değişken veya kapalı val ihtiyacı kapsamına .

Böylece x yöntemini çağırdığımızda fonksiyon değişmezinin parametresini açıkça örtük yapabiliriz

x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}

Bu, Play-Framework'ün eylem yönteminde kullanılmıştır

in view folder of app the template is declared like
@()(implicit requestHreader:RequestHeader)

in controller action is like

def index = Action{
implicit request =>

Ok(views.html.formpage())  

}

request parametresini açıkça örtük olarak belirtmezseniz,

def index = Action{
request =>

Ok(views.html.formpage()(request))  

}

4

Ayrıca, yukarıdaki durumda only one, türü olan örtük işlev olmalıdır double => Int. Aksi takdirde, derleyici karıştırılır ve düzgün derlenmez.

//this won't compile

implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0

0

Skala'daki etkilerin çok temel bir örneği.

Örtük parametreler :

val value = 10
implicit val multiplier = 3
def multiply(implicit by: Int) = value * by
val result = multiply // implicit parameter wiil be passed here
println(result) // It will print 30 as a result

Not: Burada multiplierdolaylı olarak işleve aktarılacaktır multiply. İşlev çağrısında eksik parametreler, geçerli kapsamdaki türe göre aranır; bu, kapsamda Int türünde dolaylı değişken yoksa kodun derlenmeyeceği anlamına gelir.

Örtük dönüşümler :

implicit def convert(a: Double): Int = a.toInt
val res = multiply(2.0) // Type conversions with implicit functions
println(res)  // It will print 20 as a result

Not: dediğimiz zaman multiplybir çift değer geçirerek fonksiyonunu, derleyici hangi dönüştürür geçerli kapsamda dönüşüm örtük işlevi bulmaya çalışacağız IntiçinDouble (fonksiyonu olarak multiplykabul Intparametresi). Örtük bir convertişlev yoksa, derleyici kodu derlemez.


0

Sizinle aynı soruya sahiptim ve sanırım bunu nasıl anlamaya başladığımı birkaç basit örnekle paylaşmalıyım (sadece yaygın kullanım durumlarını kapsadığını unutmayın).

Scala'da kullanılan iki yaygın kullanım durumu vardır implicit.

  • Değişken üzerinde kullanma
  • Bir işlev üzerinde kullanma

Örnekler aşağıdaki gibidir

Değişken üzerinde kullanma . Gördüğünüz gibi, implicitanahtar kelime son parametre listesinde kullanılıyorsa, en yakın değişken kullanılır.

// Here I define a class and initiated an instance of this class
case class Person(val name: String)
val charles: Person = Person("Charles")

// Here I define a function
def greeting(words: String)(implicit person: Person) = person match {
  case Person(name: String) if name != "" => s"$name, $words"
    case _ => "$words"
}

greeting("Good morning") // Charles, Good moring

val charles: Person = Person("")
greeting("Good morning") // Good moring

Bir işlev üzerinde kullanma . Gördüğünüz gibi implicit, işlevde kullanılırsa, en yakın tür dönüştürme yöntemi kullanılır.

val num = 10 // num: Int (of course)

// Here I define a implicit function
implicit def intToString(num: Int) = s"$num -- I am a String now!"

val num = 10 // num: Int (of course). Nothing happens yet.. Compiler believes you want 10 to be an Int

// Util...
val num: String = 10 // Compiler trust you first, and it thinks you have `implicitly` told it that you had a way to covert the type from Int to String, which the function `intToString` can do!
// So num is now actually "10 -- I am a String now!"
// console will print this -> val num: String = 10 -- I am a String now!

Umarım bu yardımcı olabilir.

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.