Fitur tersembunyi Scala

149

Apa saja fitur tersembunyi Scala yang harus diperhatikan oleh setiap pengembang Scala?

Tolong, satu fitur tersembunyi per jawaban.

Krzysiek Goj
sumber
6
Heh, pertanyaan ini bermanfaat untuk tautan ke pos fitur tersembunyi lainnya seperti untuk pertanyaan itu sendiri. Bersulang!
JohnMetta
1
@mettadore lihat saja tautan terkait di sisi kanan.
Daniel C. Sobral
2
@JohnMetta: Atau gunakan tag .

Jawaban:

85

Oke, saya harus menambahkan satu lagi. Setiap Regexobjek di Scala memiliki extractor (lihat jawaban dari oxbox_lakes di atas) yang memberi Anda akses ke grup pertandingan. Jadi Anda dapat melakukan sesuatu seperti:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

Baris kedua terlihat membingungkan jika Anda tidak terbiasa menggunakan pencocokan pola dan ekstraktor. Setiap kali Anda mendefinisikan valatau var, apa yang muncul setelah kata kunci bukan hanya pengidentifikasi melainkan sebuah pola. Itu sebabnya ini bekerja:

val (a, b, c) = (1, 3.14159, "Hello, world")

Ekspresi tangan kanan membuat Tuple3[Int, Double, String]yang dapat cocok dengan pola (a, b, c).

Sebagian besar waktu pola Anda menggunakan ekstraktor yang merupakan anggota objek tunggal. Misalnya, jika Anda menulis pola suka

Some(value)

maka Anda secara implisit memanggil ekstraktor Some.unapply.

Tetapi Anda juga dapat menggunakan instance kelas dalam pola, dan itulah yang terjadi di sini. Val regex adalah turunan dari Regex, dan ketika Anda menggunakannya dalam suatu pola, Anda secara implisit memanggil regex.unapplySeq( unapplyversus unapplySeqberada di luar cakupan jawaban ini), yang mengekstraksi grup pertandingan menjadi a Seq[String], elemen-elemen yang ditugaskan untuk variabel tahun, bulan, dan hari.

Willis Blackburn
sumber
1
Terima kasih untuk memposting ini! FYI disebutkan dalam bab "Mengekstraksi dengan ekspresi reguler" dalam buku "Programming in Scala" di halaman 503 di edisi pertama dan di halaman 611 di edisi kedua.
penduduk bumi
51

Definisi tipe struktural - yaitu tipe yang dijelaskan oleh metode apa yang didukungnya. Sebagai contoh:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

Perhatikan bahwa tipe parameter closeabletidak didefinisikan selain memiliki closemetode

oxbow_lakes
sumber
1
Tipe struktural bahkan tidak disebutkan dalam "Programming in Scala". Mereka sedikit lebih lambat daripada teknik lain untuk melewati jenis karena mereka menggunakan refleksi untuk memanggil metode yang tepat. (Semoga mereka akan datang dengan cara untuk mempercepat itu.)
Ken Bloom
1
Dan ada juga kemungkinan untuk membuat alias untuk mereka, yang berfungsi seperti antarmuka yang ditugaskan secara eksternal (sangat lambat): ketik Closeable = {def close (): Unit}
Alexey
45

Tipe-Konstruktor Polimorfisme (alias tipe yang lebih tinggi)

Tanpa fitur ini, Anda dapat, misalnya, mengekspresikan ide memetakan fungsi pada daftar untuk mengembalikan daftar lain, atau memetakan fungsi pada pohon untuk mengembalikan pohon lain. Tetapi Anda tidak dapat mengungkapkan ide ini secara umum tanpa jenis yang lebih tinggi.

Dengan jenis yang lebih tinggi, Anda dapat menangkap gagasan jenis apa pun yang diparameterisasi dengan jenis lain. Tipe konstruktor yang mengambil satu parameter dikatakan sejenis (*->*). Sebagai contoh List,. Tipe konstruktor yang mengembalikan tipe konstruktor lain dikatakan sejenis (*->*->*). Sebagai contoh Function1,. Tetapi dalam Scala, kami memiliki jenis yang lebih tinggi , sehingga kami dapat memiliki konstruktor tipe yang parameterised dengan konstruktor tipe lainnya. Jadi mereka seperti ((*->*)->*).

Sebagai contoh:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

Sekarang, jika Anda memiliki Functor[List], Anda dapat memetakan daftar. Jika Anda memiliki Functor[Tree], Anda dapat memetakan di atas pohon. Tetapi yang lebih penting, jika Anda memiliki Functor[A] A jenis apa pun(*->*) , Anda dapat memetakan fungsi A.

Apocalisp
sumber
39

Extractors yang memungkinkan Anda untuk mengganti if-elseif-elsekode gaya berantakan dengan pola. Saya tahu ini tidak sepenuhnya tersembunyi tetapi saya telah menggunakan Scala selama beberapa bulan tanpa benar-benar memahami kekuatan mereka. Untuk contoh (panjang) saya bisa mengganti:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

Dengan ini, yang jauh lebih jelas menurut saya

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

Saya harus melakukan sedikit kerja keras di latar belakang ...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

Tetapi kerja keras itu sepadan dengan kenyataan bahwa ia memisahkan sepotong logika bisnis menjadi tempat yang masuk akal. Saya dapat menerapkan Product.getCodemetode saya sebagai berikut ..

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
oxbow_lakes
sumber
Bukankah ini seperti saklar? mungkin ini bisa di refactored lebih banyak.
Geo
14
Pola seperti switch turbo-charged: jauh lebih kuat dan jelas
oxbow_lakes
1
Bagus, tapi saya tidak suka Anda harus menggunakan implisit karena cakupannya mencapai lebih jauh daripada pertandingan {}. Anda juga bisa menambahkan metode ke ProductService yang mencari Produk dengan kode. Anda tetap akan membungkus potongan refactored Anda dalam suatu metode untuk dapat menggunakannya di mana-mana.
Martin Konicek
35

Manifes yang merupakan semacam cara untuk mendapatkan informasi jenis saat runtime, seolah-olah Scala telah memverifikasi jenis.

oxbow_lakes
sumber
8
Saya pikir lebih baik menjelaskan jawaban dalam jawaban daripada merujuk ke tautan. Ngomong-ngomong, hai agai oxbow! :-)
Daniel C. Sobral
Ini adalah fitur yang benar-benar tersembunyi ... bahkan tidak dalam dokumen API. Sangat bermanfaat.
André Laszlo
35

Dalam scala 2.8 Anda dapat memiliki metode rekursif ekor dengan menggunakan paket scala.util.control.TailCalls (sebenarnya itu trampolining).

Sebuah contoh:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)
Aymen
sumber
35

Kelas kasus secara otomatis mencampur sifat Produk, memberikan akses terindeks, terindeks ke bidang tanpa refleksi:

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

Fitur ini juga menyediakan cara yang disederhanakan untuk mengubah output dari toStringmetode:

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 
Aaron Novstrup
sumber
32

Ini tidak sepenuhnya tersembunyi, tetapi tentu saja fitur yang diiklankan di bawah: scalac -Xprint .

Sebagai ilustrasi penggunaan pertimbangkan sumber berikut:

class A { "xx".r }

Kompilasi ini dengan scalac -Xprint: output typer :

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

Perhatikan scala.this.Predef.augmentString("xx").r, yang merupakan aplikasi dari implicit def augmentStringhadiah di Predef.scala.

scalac -Xprint: <phase> akan mencetak pohon sintaks setelah beberapa fase kompiler. Untuk melihat fase yang tersedia, gunakan scalac -Xshow-phases .

Ini adalah cara yang bagus untuk mempelajari apa yang terjadi di balik layar.

Coba dengan

case class X(a:Int,b:String)

menggunakan fase typer untuk benar-benar merasakan manfaatnya.

pedrofurla
sumber
30

Anda dapat menentukan struktur kontrol Anda sendiri. Ini benar-benar hanya fungsi dan objek dan beberapa gula sintaksis, tetapi mereka terlihat dan berperilaku seperti aslinya.

Misalnya, kode berikut mendefinisikan dont {...} unless (cond)dan dont {...} until (cond):

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

Sekarang Anda dapat melakukan hal berikut:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 
Aleksander Kmetec
sumber
Beberapa contoh lagi di sini: programmers.stackexchange.com/questions/13072/…
missingfaktor
Saya ingin tahu apakah ada yang tahu cara untuk mendefinisikan if-then-else memblokir dengan opsional lain yang ketik-cek seperti yang standar.
Philippe
@Philippe: zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero. Membutuhkan Scalaz.
missingfaktor
26

@switch anotasi dalam Scala 2.8:

Anotasi untuk diterapkan pada ekspresi kecocokan. Jika ada, kompiler akan memverifikasi bahwa pertandingan telah dikompilasi ke tableswitch atau lookupswitch, dan mengeluarkan kesalahan jika bukan mengkompilasi ke dalam serangkaian ekspresi kondisional.

Contoh:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {
missingfaktor
sumber
26

Tidak tahu apakah ini benar-benar tersembunyi, tetapi saya merasa cukup bagus.

Typeconstructors yang mengambil 2 tipe parameter dapat ditulis dalam notasi infix

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}
raichoo
sumber
1
Bagus! Saya dapat membayangkan bahwa terkadang bermanfaat dalam meningkatkan keterbacaan. Sebagai contoh var foo2barConverter: Foo ConvertTo Barakan membuat urutan parameter tipe menjadi jelas.
Esko Luontola
4
Saya kadang-kadang melakukan ini dalam kode yang menggunakan PartialFunction sampai batas tertentu: ketik ~> [A, B] = PartialFunction [A, B]
raichoo
24

Scala 2.8 memperkenalkan argumen default dan bernama, yang memungkinkan penambahan metode "copy" baru yang ditambahkan Scala ke kelas kasus. Jika Anda mendefinisikan ini:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

dan Anda ingin membuat Foo baru yang seperti Foo yang ada, hanya dengan nilai "n" yang berbeda, maka Anda bisa mengatakan:

foo.copy(n = 3)
Willis Blackburn
sumber
3
PERINGATAN: metode salin tidak akan diganti jika Anda mewarisi satu kelas kasus dari yang lain. Jadi, Anda harus menimpanya secara manual
Alexey
Terkait: Cara lebih bersih untuk memperbarui struktur bersarang stackoverflow.com/q/3900307/203968
oluies
5
kelas kasus tidak lagi (Scala 2.8) diizinkan untuk mewarisi dari kelas kasus. Terima kasih tuan Scala karena mencabut warisan yang tidak suci ini.
olle kullberg
24

dalam scala 2.8 Anda dapat menambahkan @specialized ke kelas / metode umum Anda. Ini akan membuat versi khusus kelas untuk tipe primitif (memperluas AnyVal) dan menghemat biaya tinju / unboxing yang tidak perlu: class Foo[@specialized T]...

Anda dapat memilih subset dari AnyVals: class Foo[@specialized(Int,Boolean) T]...

Aymen
sumber
1
Apakah ada penjelasan yang bisa Anda tunjukkan kepada saya? Saya ingin belajar lebih banyak.
Paweł Prażak
23

Memperluas bahasa. Saya selalu ingin melakukan sesuatu seperti ini di Jawa (tidak bisa). Tetapi dalam Scala saya dapat memiliki:

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

dan kemudian menulis:

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

dan dapatkan

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
Adrian
sumber
23

Anda dapat menetapkan parameter panggilan-dengan-nama (EDITED: ini berbeda dari parameter malas!) Ke suatu fungsi dan itu tidak akan dievaluasi sampai digunakan oleh fungsi (EDIT: pada kenyataannya, itu akan dievaluasi ulang setiap kali itu bekas). Lihat faq ini untuk detailnya

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
agilefall
sumber
Saya pikir "x: => Bar" berarti bahwa x adalah fungsi yang tidak mengambil parameter dan mengembalikan Bar. Jadi, "bar baru (22)" hanyalah fungsi anonim, dan dievaluasi sebagai fungsi seperti fungsi lainnya.
Alex Black
1
"x: () => Bar" mendefinisikan fungsi xa yang tidak mengambil parameter dan mengembalikan Bar. x: => Bar mendefinisikan x sebagai panggilan berdasarkan nama. Lihatlah scala.sygneca.com/faqs/… untuk lebih jelasnya
agilefall
3
Yang Anda perlihatkan adalah parameter panggilan-menurut-nama. Parameter malas belum diterapkan: lampsvn.epfl.ch/trac/scala/ticket/240
ArtemGr
Saya pikir Anda dapat menggunakannya sebagai param malas jika Anda melakukan sesuatu seperti lazy val xx: Bar = xdalam metode Anda dan sejak saat itu Anda hanya menggunakan xx.
Cristian Vrabie
20

Anda dapat menggunakan locallyuntuk memperkenalkan blok lokal tanpa menyebabkan masalah inferensi titik koma.

Pemakaian:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally didefinisikan dalam "Predef.scala" sebagai:

@inline def locally[T](x: T): T = x

Menjadi sejajar, itu tidak membebankan biaya tambahan apa pun.

missingfaktor
sumber
3
Ini dijelaskan lebih baik di stackoverflow.com/questions/3237727/…
Esko Luontola
17

Inisialisasi Awal:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

Keluaran:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

Kami membuat instance kelas dalam anonim, menginisialisasi valuebidang di blok, sebelum with AbstractT2klausa. Ini menjamin yang valuediinisialisasi sebelum tubuh AbstractT2dijalankan, seperti yang ditunjukkan ketika Anda menjalankan skrip.

hilangfaktor
sumber
1
Konstruk ini disebut "inisialisasi awal."
Randall Schulz
17

Anda dapat membuat tipe struktural dengan kata kunci 'with'

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}
raichoo
sumber
17

sintaksis placeholder untuk fungsi anonim

Dari Spesifikasi Bahasa Scala:

SimpleExpr1 ::= '_'

Ekspresi (dari kategori sintaksis Expr) dapat berisi simbol garis bawah yang disematkan _di tempat-tempat di mana pengidentifikasi legal. Ungkapan seperti itu mewakili fungsi anonim di mana kemunculan selanjutnya dari garis bawah menunjukkan parameter yang berurutan.

Dari Perubahan Bahasa Scala :

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

Dengan ini Anda bisa melakukan sesuatu seperti:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))
Eugene Yokota
sumber
2
Ini harus disebut sebagai 'sintaksis placeholder untuk fungsi anonim'. Tersirat memiliki makna berbeda dalam Scala, dan itu tidak terkait dengan ini.
retronym
Tautan tersebut memiliki hubungan yang tidak jelas dengan jawabannya. "implisit" bukan istilah yang tepat untuk ini. Seperti di atas seharusnya "placeholder."
Alain O'Dea
2
Ini tidak benar-benar "tersembunyi", saya telah melihat penggunaan ini di hampir semua tutorial tentang Scala yang saya baca ... :-) Tapi saya menghargai definisi formal yang belum saya lihat.
PhiLho
@ PhiLho mungkin itu kurang dikenal pada tahun 2009. saya tidak tahu.
Eugene Yokota
Saya melewatkan tanggal asli, karena hanya tanggal edit terakhir yang ditampilkan. Dan yah, tidak semua fitur yang dijelaskan di utas ini "disembunyikan". Utas keren dan jawaban yang baik pula.
PhiLho
16

Definisi tersirat, terutama konversi.

Misalnya, asumsikan fungsi yang akan memformat string input agar sesuai dengan ukuran, dengan mengganti bagian tengahnya dengan "...":

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Anda dapat menggunakannya dengan String apa pun, dan, tentu saja, gunakan metode toString untuk mengonversi apa saja. Tapi Anda juga bisa menulisnya seperti ini:

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Dan kemudian, Anda bisa lulus kelas tipe lain dengan melakukan ini:

implicit def double2String(d: Double) = d.toString

Sekarang Anda dapat memanggil fungsi itu lewat ganda:

sizeBoundedString(12345.12345D, 8)

Argumen terakhir adalah implisit, dan diajukan secara otomatis karena deklarasi implisit de. Selanjutnya, "s" diperlakukan seperti sebuah String di dalam sizeBoundedString karena ada konversi implisit dari itu ke String.

Implikasi dari tipe ini didefinisikan lebih baik untuk tipe yang tidak umum untuk menghindari konversi yang tidak terduga. Anda juga dapat secara eksplisit melewatkan konversi, dan itu masih akan digunakan secara implisit di dalam sizeBoundedString:

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

Anda juga dapat memiliki beberapa argumen implisit, tetapi kemudian Anda harus melewati semuanya, atau tidak melewati salah satunya. Ada juga sintaks pintasan untuk konversi implisit:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Ini digunakan dengan cara yang persis sama.

Implikasi dapat memiliki nilai apa pun. Mereka dapat digunakan, misalnya, untuk menyembunyikan informasi perpustakaan. Ambil contoh berikut, misalnya:

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

Dalam contoh ini, memanggil "f" dalam objek Y akan mengirim log ke daemon default, dan pada instance X ke daemon X Daemon. Tetapi memanggil g pada instance X akan mengirim log ke DefaultDaemon yang diberikan secara eksplisit.

Sementara contoh sederhana ini dapat ditulis ulang dengan overload dan private state, implisit tidak memerlukan private state, dan dapat dibawa ke dalam konteks dengan impor.

Daniel C. Sobral
sumber
13

Mungkin tidak terlalu tersembunyi, tapi saya pikir ini berguna:

@scala.reflect.BeanProperty
var firstName:String = _

Ini akan secara otomatis menghasilkan pengambil dan penyetel untuk bidang yang cocok dengan konvensi kacang.

Penjelasan lebih lanjut di developerworks

agilefall
sumber
6
Dan Anda dapat membuat pintasan untuk itu jika Anda sering menggunakannya, misal: import scala.reflect. {BeanProperty => BP}
Alexey
13

Argumen implisit dalam penutupan.

Argumen fungsi dapat ditandai sebagai implisit seperti halnya metode. Dalam ruang lingkup fungsi fungsi parameter implisit terlihat dan memenuhi syarat untuk resolusi implisit:

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}
axel22
sumber
12

Jenis hasil tergantung pada resolusi implisit. Ini dapat memberi Anda bentuk pengiriman ganda:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0
jsuereth
sumber
Mungkin itu masalahnya, tetapi sesi di atas menyesatkan. Definisi foopenggunaan dan ayang harus ada di lingkungan sebelum pelaksanaan perintah-perintah ini. Saya menganggap Anda maksud z.perform(x).
Daniel C. Sobral
4

Setara dengan Scala untuk Java double brace initializer.

Scala memungkinkan Anda untuk membuat subclass anonim dengan tubuh kelas (konstruktor) yang berisi pernyataan untuk menginisialisasi instance kelas itu.

Pola ini sangat berguna ketika membangun antarmuka pengguna berbasis komponen (misalnya Swing, Vaadin) karena memungkinkan untuk membuat komponen UI dan mendeklarasikan propertinya lebih ringkas.

Lihat http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf untuk informasi lebih lanjut.

Berikut adalah contoh membuat tombol Vaadin:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}
Guillaume Belrose
sumber
3

Tidak termasuk anggota dari importpernyataan

Misalkan Anda ingin menggunakan Loggeryang berisi a printlndan printerrmetode, tetapi Anda hanya ingin menggunakan yang untuk pesan kesalahan, dan menyimpan yang lama baik Predef.printlnuntuk keluaran standar. Anda bisa melakukan ini:

val logger = new Logger(...)
import logger.printerr

tetapi jika loggerjuga mengandung dua belas metode lain yang ingin Anda impor dan gunakan, menjadi tidak nyaman untuk mendaftarnya. Anda bisa mencoba:

import logger.{println => donotuseprintlnt, _}

tetapi ini masih "mencemari" daftar anggota yang diimpor. Masukkan wildcard kuat über:

import logger.{println => _, _}

dan itu akan melakukan hal yang benar ™.

Philippe
sumber
2

requiremetode (didefinisikan dalam Predef) yang memungkinkan Anda untuk mendefinisikan kendala fungsi tambahan yang akan diperiksa selama waktu berjalan. Bayangkan Anda sedang mengembangkan klien twitter lain dan Anda perlu membatasi panjang tweet hingga 140 simbol. Selain itu Anda tidak dapat memposting tweet kosong.

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

Sekarang memanggil pos dengan argumen panjang yang tidak sesuai akan menyebabkan pengecualian:

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

Anda dapat menulis beberapa persyaratan atau bahkan menambahkan deskripsi ke masing-masing:

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

Sekarang pengecualian bertele-tele:

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

Satu contoh lagi ada di sini .


Bonus

Anda dapat melakukan tindakan setiap kali persyaratan gagal:

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1
om-nom-nom
sumber
1
requirebukan kata yang dilindungi undang-undang. Ini hanyalah metode yang didefinisikan dalam Predef.
missingfaktor
1

Ciri-ciri dengan abstract overridemetode adalah fitur dalam Scala yang tidak banyak diiklankan seperti halnya banyak lainnya. Maksud dari metode dengan abstract overridepengubah adalah untuk melakukan beberapa operasi dan mendelegasikan panggilan ke super. Maka sifat-sifat ini harus dicampur dengan implementasi konkret dari abstract overridemetode mereka .

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

Walaupun contoh saya benar-benar tidak lebih dari AOP orang miskin, saya menggunakan Ciri - ciri Stackable ini sesuai dengan keinginan saya untuk membangun contoh juru bahasa Scala dengan impor yang telah ditentukan sebelumnya, binding kustom, dan jalur kelas. The Stackable Traits memungkinkan untuk membuat pabrik saya sepanjang garis new InterpreterFactory with JsonLibs with LuceneLibsdan kemudian memiliki impor berguna dan ruang lingkup varibles untuk pengguna script.

MXFr
sumber