อะไรคือคุณสมบัติที่ซ่อนอยู่ของ Scala ที่นักพัฒนา Scala ทุกคนควรระวัง
กรุณาซ่อนคุณสมบัติหนึ่งข้อต่อคำตอบ
อะไรคือคุณสมบัติที่ซ่อนอยู่ของ Scala ที่นักพัฒนา Scala ทุกคนควรระวัง
กรุณาซ่อนคุณสมบัติหนึ่งข้อต่อคำตอบ
คำตอบ:
โอเคฉันต้องเพิ่มอีกหนึ่งรายการ Regex
วัตถุทุกชิ้นใน Scala มีตัวแยกข้อมูล (ดูคำตอบจาก oxbox_lakes ด้านบน) ที่ให้คุณเข้าถึงกลุ่มการจับคู่ ดังนั้นคุณสามารถทำสิ่งที่ชอบ:
// 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"
บรรทัดที่สองดูสับสนหากคุณไม่คุ้นเคยกับการใช้การจับคู่รูปแบบและตัวแยกไฟล์ เมื่อใดก็ตามที่คุณกำหนดval
หรือvar
สิ่งที่เกิดขึ้นหลังจากคำหลักนั้นไม่ได้เป็นเพียงตัวระบุ แต่เป็นรูปแบบ นั่นเป็นเหตุผลที่ทำงานนี้:
val (a, b, c) = (1, 3.14159, "Hello, world")
การแสดงออกทางด้านขวามือสร้างซึ่งสามารถตรงกับรูปแบบTuple3[Int, Double, String]
(a, b, c)
ส่วนใหญ่รูปแบบของคุณใช้ตัวแยกที่เป็นสมาชิกของวัตถุเดี่ยว ตัวอย่างเช่นถ้าคุณเขียนรูปแบบเช่น
Some(value)
Some.unapply
แล้วคุณโดยปริยายเรียกระบาย
แต่คุณยังสามารถใช้คลาสอินสแตนซ์ในรูปแบบและนั่นคือสิ่งที่เกิดขึ้นที่นี่ val regex เป็นตัวอย่างของRegex
และเมื่อคุณใช้ในรูปแบบคุณโทรโดยปริยายregex.unapplySeq
( unapply
แทนที่จะunapplySeq
อยู่นอกเหนือขอบเขตของคำตอบนี้) ซึ่งแยกกลุ่มการจับคู่เป็นSeq[String]
องค์ประกอบที่ได้รับมอบหมายเพื่อ ตัวแปรปีเดือนและวัน
นิยามประเภทโครงสร้าง - เช่นชนิดที่อธิบายโดยวิธีการที่รองรับ ตัวอย่างเช่น:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
ขอให้สังเกตว่าประเภทของพารามิเตอร์closeable
ไม่ได้กำหนดไว้นอกจากที่มีclose
วิธีการ
ยกตัวอย่างเช่นหากไม่มีฟีเจอร์นี้คุณสามารถแสดงความคิดในการทำแผนที่ฟังก์ชั่นเหนือรายการเพื่อส่งคืนรายการอื่นหรือทำแผนที่ฟังก์ชั่นบนต้นไม้เพื่อส่งคืนต้นไม้อื่น แต่คุณไม่สามารถแสดงความคิดเห็นนี้โดยทั่วไปหากไม่มีประเภทที่สูงขึ้น
ด้วยชนิดที่สูงกว่าคุณสามารถจับภาพความคิดประเภทใด ๆที่กำหนดพารามิเตอร์ด้วยประเภทอื่น (*->*)
ตัวสร้างประเภทที่ใช้พารามิเตอร์หนึ่งจะกล่าวว่าเป็นของชนิด ตัวอย่างเช่นList
. (*->*->*)
ตัวสร้างประเภทที่ส่งกลับประเภทคอนสตรัคอื่นจะกล่าวว่าเป็นของชนิด ตัวอย่างเช่นFunction1
. แต่ใน Scala เรามีชนิดที่สูงกว่าดังนั้นเราจึงสามารถมีตัวสร้างประเภทที่มีพารามิเตอร์กับตัวสร้างชนิดอื่น ((*->*)->*)
ดังนั้นพวกเขากำลังชนิดเช่น
ตัวอย่างเช่น:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
ตอนนี้ถ้าคุณมีFunctor[List]
คุณสามารถแมปผ่านรายการต่างๆ หากคุณมีFunctor[Tree]
คุณสามารถแผนที่เหนือต้นไม้ แต่ที่สำคัญกว่านั้นคือถ้าคุณมีFunctor[A]
ชนิด A(*->*)
คุณสามารถแมปฟังก์ชั่นA
ได้
Extractorsซึ่งอนุญาตให้คุณแทนที่if-elseif-else
โค้ดลักษณะยุ่งๆ ด้วยรูปแบบ ฉันรู้ว่าสิ่งเหล่านี้ไม่ได้ซ่อนอยู่แต่ฉันใช้ Scala มาสองสามเดือนโดยไม่เข้าใจพลังของพวกเขาจริงๆ สำหรับตัวอย่าง (ยาว) ฉันสามารถแทนที่:
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)
}
ด้วยสิ่งนี้ซึ่งชัดเจนมากในความคิดของฉัน
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
ฉันต้องทำ legwork เล็กน้อยในพื้นหลัง ...
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
}
}
แต่ legwork นั้นมีค่าสำหรับความจริงที่ว่ามันแยกตรรกะทางธุรกิจออกเป็นส่วน ๆ ฉันสามารถใช้Product.getCode
วิธีการของฉันดังต่อไปนี้ ..
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
รายการที่เป็นวิธีในการรับข้อมูลชนิดที่รันไทม์ราวกับว่า Scala มีประเภท reified
ใน scala 2.8 คุณสามารถมีวิธีการแบบวนซ้ำโดยใช้แพ็คเกจ scala.util.control.TailCalls (อันที่จริงมันเป็น trampolining)
ตัวอย่าง:
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)
คลาสเคสจะผสมคุณลักษณะของผลิตภัณฑ์โดยอัตโนมัติโดยให้การเข้าถึงดัชนีที่ไม่มีการทำดัชนีในฟิลด์โดยไม่มีการสะท้อนใด ๆ :
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)
คุณลักษณะนี้ยังให้วิธีที่ง่ายขึ้นในการแก้ไขผลลัพธ์ของtoString
วิธีการ:
case class Person(name: String, age: Int) {
override def productPrefix = "person: "
}
// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28))
มันไม่ได้ซ่อนว่า แต่แน่นอนภายใต้คุณลักษณะโฆษณา: scalac -Xprint
ตามภาพประกอบการใช้งานให้พิจารณาแหล่งข้อมูลต่อไปนี้:
class A { "xx".r }
รวบรวมสิ่งนี้ด้วยscalac -Xprint: typerเอาท์พุต:
package <empty> {
class A extends java.lang.Object with ScalaObject {
def this(): A = {
A.super.this();
()
};
scala.this.Predef.augmentString("xx").r
}
}
คำบอกกล่าวscala.this.Predef.augmentString("xx").r
ซึ่งเป็นimplicit def augmentString
การนำเสนอใน Predef.scala
scalac -Xprint: <phase>จะพิมพ์แผนผังไวยากรณ์หลังจากคอมไพเลอร์เฟส หากต้องการดูขั้นตอนที่มีอยู่ใช้scalac -Xshow
นี่เป็นวิธีที่ดีในการเรียนรู้สิ่งที่เกิดขึ้นเบื้องหลัง
ลองด้วย
case class X(a:Int,b:String)
การใช้เฟสtyperเพื่อให้รู้สึกว่ามีประโยชน์จริงๆ
คุณสามารถกำหนดโครงสร้างการควบคุมของคุณเอง มันเป็นแค่ฟังก์ชั่นและวัตถุและน้ำตาลประโยค แต่พวกมันดูและประพฤติตัวเหมือนของจริง
ตัวอย่างเช่นรหัสต่อไปนี้กำหนดdont {...} unless (cond)
และ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
}
}
ตอนนี้คุณสามารถทำสิ่งต่อไปนี้:
/* 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)
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
@Philippe: ต้องการ Scalaz
@switch
คำอธิบายประกอบใน Scala 2.8:
คำอธิบายประกอบที่จะใช้กับนิพจน์การจับคู่ หากมีอยู่คอมไพเลอร์จะตรวจสอบว่าการจับคู่ถูกรวบรวมไปยัง tableswitch หรือ lookupswitch และออกข้อผิดพลาดถ้ามันรวบรวมคอมไพล์ลงในชุดของนิพจน์ที่มีเงื่อนไขแทน
ตัวอย่าง:
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 {
Dunno ถ้านี่ถูกซ่อนอยู่จริงๆ แต่ฉันคิดว่ามันค่อนข้างดี
Typeconstructors ที่ใช้พารามิเตอร์ 2 ชนิดสามารถเขียนได้ในรูปแบบของ 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
}
}
var foo2barConverter: Foo ConvertTo Bar
จะทำให้ลำดับของพารามิเตอร์ประเภทตนเองชัดเจน
Scala 2.8 นำเสนอค่าเริ่มต้นและอาร์กิวเมนต์ที่ตั้งชื่อซึ่งทำให้มีความเป็นไปได้ที่จะเพิ่มวิธีการ "คัดลอก" ใหม่ที่ Scala เพิ่มให้กับคลาสเคส หากคุณกำหนดสิ่งนี้:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
และคุณต้องการสร้าง Foo ใหม่ที่เหมือนกับ Foo ที่มีอยู่แล้วเท่านั้นที่มีค่า "n" ที่แตกต่างจากนั้นคุณสามารถพูดได้ว่า:
foo.copy(n = 3)
ใน scala 2.8 คุณสามารถเพิ่ม @specialized ให้กับคลาส / วิธีการทั่วไปของคุณได้ สิ่งนี้จะสร้างรุ่นพิเศษของคลาสสำหรับประเภทดั้งเดิม (ขยาย AnyVal) และประหยัดค่าใช้จ่ายในการชกมวย / การยกเลิกการ Unbox ที่ไม่จำเป็น:
class Foo[@specialized T]...
คุณสามารถเลือกชุดย่อยของ AnyVals:
class Foo[@specialized(Int,Boolean) T]...
การขยายภาษา ฉันอยากทำบางสิ่งเช่นนี้ใน Java (ทำไม่ได้) แต่ใน Scala ฉันสามารถมี:
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
}
แล้วเขียน:
val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed { // "timed" is a new "keyword"!
numbers.sortWith(_<_)
}
println(sorted)
และรับ
Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
คุณสามารถกำหนดพารามิเตอร์ call-by-name (แก้ไข: นี่แตกต่างจากพารามิเตอร์ที่ขี้เกียจ!) ไปยังฟังก์ชั่นและจะไม่ได้รับการประเมินจนกว่าจะใช้โดยฟังก์ชั่น (แก้ไข: จริง ๆ แล้วมันจะถูกประเมินค่าใหม่ทุกครั้ง ใช้) ดูคำถามที่พบบ่อยนี้สำหรับรายละเอียด
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
*/
lazy val xx: Bar = x
xx
คุณสามารถใช้locally
เพื่อแนะนำบล็อกท้องถิ่นโดยไม่ก่อให้เกิดปัญหาการอนุมานอัฒภาค
การใช้งาน:
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
ถูกกำหนดใน "Predef.scala" เป็น:
@inline def locally[T](x: T): T = x
เป็นแบบอินไลน์จะไม่กำหนดค่าใช้จ่ายเพิ่มเติมใด ๆ
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)
เอาท์พุท:
In AbstractT2:
AbstractT2: value = 10, inverse = 0.1
c2c.value = 10, inverse = 0.1
เรายกตัวอย่างคลาสภายในที่ไม่ระบุชื่อเริ่มต้น
value
ฟิลด์ในบล็อกก่อนส่วนwith AbstractT2
คำสั่ง สิ่งนี้รับประกันว่าvalue
จะเริ่มต้นได้ก่อนที่เนื้อความของAbstractT2
จะถูกดำเนินการตามที่แสดงเมื่อคุณเรียกใช้สคริปต์
คุณสามารถเขียนประเภทโครงสร้างด้วยคำหลัก '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
}
}
ไวยากรณ์ตัวยึดตำแหน่งสำหรับฟังก์ชั่นที่ไม่ระบุชื่อ
จากข้อกำหนดภาษา Scala:
SimpleExpr1 ::= '_'
นิพจน์ (ของหมวดหมู่ประโยค
Expr
) อาจมีสัญลักษณ์ขีดล่างฝังอยู่_
ในสถานที่ที่ตัวระบุถูกกฎหมาย การแสดงออกดังกล่าวหมายถึงฟังก์ชั่นที่ไม่ระบุชื่อที่เกิดขึ้นภายหลังของขีดล่างแสดงถึงพารามิเตอร์ที่ต่อเนื่อง
_ + 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)
เมื่อใช้สิ่งนี้คุณสามารถทำสิ่งต่อไปนี้:
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
คำจำกัดความโดยนัยโดยเฉพาะอย่างยิ่งการแปลง
ตัวอย่างเช่นสมมติว่าฟังก์ชันที่จะจัดรูปแบบสตริงอินพุตให้พอดีกับขนาดโดยแทนที่ตรงกลางด้วย "... ":
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
}
คุณสามารถใช้สิ่งนั้นกับสตริงใด ๆ และแน่นอนใช้เมธอด toString เพื่อแปลงอะไรก็ได้ แต่คุณสามารถเขียนมันแบบนี้ได้:
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
}
จากนั้นคุณสามารถผ่านคลาสอื่น ๆ โดยทำสิ่งนี้:
implicit def double2String(d: Double) = d.toString
ตอนนี้คุณสามารถเรียกใช้ฟังก์ชันที่ผ่านสองครั้ง:
sizeBoundedString(12345.12345D, 8)
อาร์กิวเมนต์สุดท้ายคือ implicit และถูกส่งโดยอัตโนมัติเนื่องจากการประกาศ de implicit นอกจากนี้ "s" กำลังได้รับการปฏิบัติเหมือนสตริงภายใน sizeBoundedString เนื่องจากมีการแปลงโดยนัยจากมันเป็นสตริง
การระบุประเภทนี้มีการกำหนดที่ดีกว่าสำหรับประเภทที่ผิดปกติเพื่อหลีกเลี่ยงการแปลงที่ไม่คาดคิด คุณสามารถส่งผ่านการแปลงได้อย่างชัดเจนและจะยังคงถูกใช้ภายใน sizeBoundedString:
sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)
คุณสามารถมีข้อโต้แย้งโดยนัยได้หลายข้อ แต่จากนั้นคุณต้องส่งผ่านข้อโต้แย้งทั้งหมดหรือไม่ผ่านข้อใดข้อหนึ่ง นอกจากนี้ยังมีไวยากรณ์ทางลัดสำหรับการแปลงโดยนัย:
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
}
นี่ใช้วิธีเดียวกันทั้งหมด
การนัยสามารถมีค่าใด ๆ ตัวอย่างเช่นสามารถใช้เพื่อซ่อนข้อมูลไลบรารี ยกตัวอย่างเช่น
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")
}
}
ในตัวอย่างนี้การเรียก "f" ในวัตถุ Y จะส่งบันทึกไปยัง daemon เริ่มต้นและบนอินสแตนซ์ของ X ไปยัง Daemon X daemon แต่การเรียก g บนอินสแตนซ์ของ X จะส่งบันทึกไปยัง DefaultDaemon ที่กำหนดไว้อย่างชัดเจน
ในขณะที่ตัวอย่างง่ายๆนี้สามารถเขียนใหม่ด้วย overload และสถานะส่วนตัว implicits ไม่ต้องการสถานะส่วนตัวและสามารถนำเข้าบริบทกับการนำเข้า
อาจจะไม่ซ่อนเร้น แต่ฉันคิดว่ามันมีประโยชน์:
@scala.reflect.BeanProperty
var firstName:String = _
สิ่งนี้จะสร้างตัวเรียกและตัวตั้งค่าสำหรับฟิลด์ที่ตรงกับการประชุม bean โดยอัตโนมัติ
คำอธิบายเพิ่มเติมที่developerworks
ข้อโต้แย้งโดยนัยในการปิด
อาร์กิวเมนต์ของฟังก์ชันสามารถทำเครื่องหมายเป็นนัยได้เช่นเดียวกับเมธอด ภายในขอบเขตของเนื้อความของฟังก์ชันพารามิเตอร์ implicit สามารถมองเห็นและมีสิทธิ์ได้รับการแก้ไขโดยนัย:
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")
})
}
สร้างโครงสร้างข้อมูลที่ไม่มีขีด จำกัด ด้วย Scala's Stream
:
http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient
ประเภทผลลัพธ์ขึ้นอยู่กับการแก้ไขโดยนัย สิ่งนี้สามารถให้รูปแบบการจัดส่งหลายแบบแก่คุณ:
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
foo
ใช้งานa
ซึ่งจะต้องมีอยู่ในสภาพแวดล้อมก่อนที่จะดำเนินการตามคำสั่งเหล่านี้ z.perform(x)
ผมถือว่าคุณหมาย
Scala ช่วยให้คุณสร้างคลาสย่อยแบบไม่ระบุชื่อด้วยเนื้อความของคลาส (ตัวสร้าง) ที่มีคำสั่งเพื่อเริ่มต้นอินสแตนซ์ของคลาสนั้น
รูปแบบนี้มีประโยชน์มากเมื่อสร้างส่วนต่อประสานผู้ใช้ที่เป็นส่วนประกอบ (เช่น Swing, Vaadin) เนื่องจากช่วยให้สามารถสร้างส่วนประกอบ UI และประกาศคุณสมบัติของพวกเขาได้อย่างกระชับ
ดูhttp://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdfสำหรับข้อมูลเพิ่มเติม
นี่คือตัวอย่างของการสร้างปุ่ม Vaadin:
val button = new Button("Click me"){
setWidth("20px")
setDescription("Click on this")
setIcon(new ThemeResource("icons/ok.png"))
}
import
ข้อความสั่งสมมติว่าคุณต้องการใช้ a Logger
ที่มี a println
และ a printerr
method แต่คุณต้องการที่จะใช้มันสำหรับข้อความแสดงความผิดพลาดเท่านั้นและทำให้เก่าดีPredef.println
สำหรับเอาท์พุทมาตรฐาน คุณสามารถทำสิ่งนี้:
val logger = new Logger(...)
import logger.printerr
แต่ถ้าlogger
มีอีกสิบสองวิธีที่คุณต้องการนำเข้าและใช้มันจะไม่สะดวกในการแสดงรายการ คุณสามารถลองแทน:
import logger.{println => donotuseprintlnt, _}
แต่นี่ยัง "มลภาวะ" รายการของสมาชิกที่นำเข้า ป้อนไวด์การ์ดที่ทรงพลังüber:
import logger.{println => _, _}
และนั่นจะทำในสิ่งที่ถูกต้อง ™
require
วิธีการ (กำหนดไว้ในPredef
) ที่ช่วยให้คุณสามารถกำหนดข้อ จำกัด ฟังก์ชั่นเพิ่มเติมที่จะตรวจสอบในช่วงเวลาทำงาน ลองนึกภาพว่าคุณกำลังพัฒนาลูกค้าตัวสั่นด้วยความตื่นเต้นคนอื่นและคุณจำเป็นต้องจำกัดความยาวทวีตไม่เกิน 140 สัญลักษณ์ ยิ่งกว่านั้นคุณไม่สามารถโพสต์ทวีตที่ว่างเปล่า
def post(tweet: String) = {
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
}
ตอนนี้โทรโพสต์ด้วยอาร์กิวเมนต์ความยาวไม่เหมาะสมจะทำให้เกิดข้อยกเว้น:
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)
คุณสามารถเขียนข้อกำหนดหลายข้อหรือเพิ่มรายละเอียดให้กับแต่ละข้อได้:
def post(tweet: String) = {
require(tweet.length > 0, "too short message")
require(tweet.length < 140, "too long message")
println(tweet)
}
ตอนนี้มีข้อยกเว้น verbose:
scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:8)
คุณสามารถทำการกระทำได้ทุกครั้งที่ความต้องการล้มเหลว:
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
require
ไม่ใช่คำที่สงวนไว้ Predef
แต่มันเป็นวิธีการที่กำหนดไว้ใน
คุณลักษณะที่มีabstract override
วิธีการเป็นคุณลักษณะใน Scala ที่ไม่ได้โฆษณากันอย่างแพร่หลายเหมือนคนอื่น ๆ ตั้งใจของวิธีการที่มีปรับปรุงคือการทำดำเนินการบางอย่างและการมอบหมายการเรียกร้องให้abstract override
super
จากนั้นคุณสมบัติเหล่านี้จะต้องมีการผสมกับการใช้งานที่เป็นรูปธรรมของabstract override
วิธีการของพวกเขา
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
ในขณะที่ตัวอย่างของฉันไม่มากไปกว่าชายที่ยากจน AOP แต่ฉันใช้คุณลักษณะแบบเรียงซ้อนได้เหล่านี้ตามความชอบของฉันในการสร้างอินสแตนซ์ล่ามสกาล่าด้วยการนำเข้าที่กำหนดไว้ล่วงหน้าการผูกแบบกำหนดเองและคลาสพา ธ ซ้อนลักษณะทำให้มันเป็นไปได้ที่จะสร้างโรงงานของฉันตามสายของnew InterpreterFactory with JsonLibs with LuceneLibs
แล้วมีการนำเข้าที่มีประโยชน์และขอบเขต varibles สำหรับผู้ใช้สคริปต์