스칼라의 암시 적 이해


308

나는 Scala playframework tutorial을 통해 길을 가고 있었고 나는이 코드 조각을 보았습니다.

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

그래서 나는 조사하기로 결정 하고이 게시물을 가로 질러왔다 .

나는 아직도 그것을 얻지 못한다.

이것의 차이점은 무엇입니까?

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

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

명백한 사실 외에 다른 방법 이름이 있습니다.

언제 implicit, 왜 사용해야 합니까?


이 튜토리얼이 정말 유용하다는 것을 알았습니다 : Scala Tutorial-암시 적 함수를 만드는 방법 배우기
Adrian Moisa

답변:


391

아래에서는 암시 적 주요 사용 사례를 설명하지만보다 자세한 내용은 Scala 프로그래밍 관련 장을 참조하십시오 .

암시 적 매개 변수

메소드의 최종 매개 변수 목록은로 표시 될 수 있으며 implicit이는 값이 호출되는 컨텍스트에서 값을 가져옴 을 의미합니다. 범위 내에 올바른 유형의 암시 적 값이 없으면 컴파일되지 않습니다. 암시 적 값은 단일 값으로 해석되고 충돌을 피해야하므로 유형을 목적에 맞게 지정하는 것이 좋습니다 Int.

예:

  // 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"

암시 적 변환

컴파일러가 컨텍스트에 대해 잘못된 유형의 표현식을 찾으면 Function유형을 확인할 수있는 유형 의 내재 된 값을 찾습니다 . 따라서 A가 필요하고을 찾으면 범위 내에서 B유형의 암시 적 값을 찾습니다 B => A( BA동반자 객체 와 같은 다른 위치도 확인 ). defs는 Function객체 로 "eta-expanded"될 수 있기 때문에 implicit def xyz(arg: B): A의지도 있습니다.

따라서 메소드의 차이점은 implicita Double가 발견되었지만 a Int가 필요할 때 표시된 메소드가 컴파일러에 의해 삽입된다는 것 입니다.

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

와 동일하게 작동합니다

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

두 번째로 변환을 수동으로 삽입했습니다. 처음에는 컴파일러가 자동으로 동일한 작업을 수행했습니다. 왼쪽의 유형 주석으로 인해 변환이 필요합니다.


Play의 첫 번째 스 니펫 관련 :

이 페이지의 작업은 Play 설명서에서 설명 합니다 ( API 문서 참조 ). 사용하고 있습니다

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

Action(같은 이름의 형질 컴패니언) 개체.

따라서 함수를 인수로 제공해야하며 이는 형식으로 리터럴로 작성 될 수 있습니다.

request => ...

함수 리터럴에서 앞에있는 부분 =>은 값 선언이며 implicit다른 val선언 과 마찬가지로 원하는 경우 표시 할 수 있습니다 . 여기에, request 하지 않습니다 표시 할 필요가 implicit이러한 유형의 검사를위한, 그러나 수 있도록 수행하여 암시 적 값으로 사용할 수 함수 내 필요할 수있는 방법에 대한 (물론, 그것은뿐만 아니라 명시 적으로 사용할 수 있습니다) . 이 특정한 경우 Form 클래스 의 bindFromRequest메서드에 암시적인 인수 가 필요 하기 때문에이 작업이 수행되었습니다 .Request


12
답변 주셔서 감사합니다. 21 장의 링크는 정말 대단합니다. 감사합니다.
Clive

14
이것을 추가하기 위해 다음 비디오는 암시 적에 대한 훌륭한 설명과 scala의 다른 기능 youtube.com/watch?v=IobLWVuD-CQ
Shakti

위의 비디오에서 24:25 로 이동 하십시오 (55 분 동안 듣고 싶지 않은 사람들을 위해)
papigee

36

경고 : 풍자 만화를 신중하게 포함합니다! YMMV ...

루이지의 답변 은 완전하고 정확합니다. 이것은 스칼라 프로젝트에서 자주 발생하기 때문에 암시 적을 과도하게 과도하게 과도하게 사용하는 방법에 대한 예제로 약간 확장하는 것 입니다. 실제로는 "Best Practice" 가이드 중 하나에서 찾을 수도 있습니다 .

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
ㅋ. 유머 감각이 좋습니다.
Det

1
유머 감사합니다. 이런 종류의 일이 몇 년 전에 스칼라를 배우려고 시도하지 않은 이유 중 하나입니다. 내가보고있는 코드에서 암시 적 중 일부가 어디에서 왔는지 확신하지 못했습니다.
melston

7

request매개 변수를 implicit다음 과 같이 표시해야하는 이유와시기 :

작업 본문에서 사용할 일부 메서드에는 다음 과 같은 암시 적 매개 변수 목록 이 있습니다. 예를 들어 Form.scala는 메서드를 정의합니다.

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

방금 전화를하는 것처럼 당신은 반드시이 통지를하지 않는 myForm.bindFromRequest()당신은 명시 적으로 암시 인수를 제공 할 필요가 없습니다. 아니요, 요청 인스턴스가 필요한 메소드 호출이 올 때마다 전달할 유효한 후보 오브젝트를 찾도록 컴파일러 를 남겨 둡니다 . 당신이 있기 때문에 요청이 가능한, 당신이 오직 할 필요가있는 것으로 표시하는 것입니다 implicit.

암시 적 으로 사용할 수있는 것으로 명시 적으로 표시합니다 .

당신은 컴파일러가 Play 프레임 워크에서 보낸 요청 객체 ( "request"라는 이름을 주 었으나 "r"또는 "req"만 사용했을 수 있음)를 필요로하는 곳에 "sly" .

myForm.bindFromRequest()

보여? 거기 아니지만, 그것은 이다 거기!

그것은 단지 당신이 (하지만 당신이 필요한 모든 장소에서 수동으로 그것을 슬롯 필요없이 발생 할 수 있습니다 당신이 만약 소원 때문에, 아무리 그것이 표시 않다면, 명시 적으로 통과 implicit여부) :

myForm.bindFromRequest()(request)

암시 적으로 표시하지 않으면 위의 작업 수행해야합니다. 암시 적으로 표시하지 않아도됩니다.

당신은 요청을 표시해야합니다 implicit? Request의 인스턴스를 예상하는 암시 적 매개 변수 목록 을 선언하는 메소드를 사용하는 경우에만 필요합니다 . 그러나 간단하게 유지하려면 요청을 implicit 항상 표시하는 습관을 들일 수 있습니다 . 그렇게하면 아름다운 간결한 코드를 작성할 수 있습니다.


2
"그런 식으로 아름다운 간결한 코드를 작성할 수 있습니다." 또는 @DanielDinnyes가 지적했듯이 아름답게 난독 화 된 코드입니다. 암시 적 출처를 추적하는 것은 정말 고통스럽고 조심하지 않으면 실제로 코드를 읽고 유지하기가 더 어려워 질 수 있습니다.
melston

7

스칼라에서는 암시 적으로 다음 과 같이 작동합니다 .

변환기

매개 변수 값 인젝터

암시 적 사용에는 3 가지 유형이 있습니다.

  1. 암시 적으로 유형 변환 : 오류 생성 할당을 의도 한 유형으로 변환합니다.

    val x : String = "1"

    val y : Int = x

문자열Int하위 유형 이 아니므로 2 행에서 오류가 발생합니다. 오류를 해결하기 위해 컴파일러는 묵시적 키워드가 있고 문자열 을 인수로 사용하고 Int를 리턴하는 범위에서 이러한 메소드를 찾습니다 .

그래서

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. 암시 적 수신자 변환 : 일반적으로 수신자 호출 오브젝트의 특성 (예 : 메소드 또는 변수. 따라서 수신자가 속성을 호출하려면 해당 속성이 해당 수신자의 클래스 / 객체의 멤버 여야합니다.

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

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi



   mahadi.haveTv // Error happening

여기서 mahadi.haveTv 는 오류를 생성합니다. 스칼라 컴파일러는 먼저 hasHv 속성을 mahadi 수신기 로 찾습니다 . 찾을 수 없습니다. 두 번째로 Mahadi 객체 를 인수로 사용하고 Johnny 객체를 반환 하는 암시 적 키워드가 있는 범위의 메소드를 찾습니다 . 그러나 여기에는 없습니다. 따라서 오류가 발생 합니다. 그러나 다음은 괜찮습니다.

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. 암시 적 매개 변수 주입 : 메서드를 호출하고 매개 변수 값을 전달하지 않으면 오류가 발생합니다. 스칼라 컴파일러는 다음과 같이 작동합니다-먼저 값을 전달하려고 시도하지만 매개 변수에 대한 직접적인 값은 얻지 않습니다.

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

둘째, 매개 변수에 암시 적 키워드가 있으면 범위 에서 동일한 유형 의 값 을 가진 모든 val 을 찾습니다 . 그렇지 않으면 오류가 발생합니다.

def x(implicit a:Int)= a

x // error happening here

이 문제를 해결하기 위해 컴파일러는 매개 변수 a암시 적 키워드 가 있기 때문에 Int 유형의 암시 적 val 을 찾습니다 .

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.

다른 예시:

def l(implicit b:Int)

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

우리는 또한 이렇게 쓸 수 있습니다.

def x(implicit a:Int)= l

때문에 갖는 암시 파라미터 와의 범위에 있어서의 X의 신체 ,가 암시 로컬 변수 ( 파라미터 로컬 변수 ) 의 파라미터 인 X 그래서에서 의 X의 본체 방법 에있어서 시그너처 L의 암시 적 인수 값 이며 x 메소드의 로컬 암시 적 변수 (매개 변수)에 의해 암시 적으로 제출됩니다 . a

그래서

 def x(implicit a:Int)= l

이 같은 컴파일러에있을 것입니다

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

다른 예시:

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

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

x{
x => c
}

이 때문에, 에러가 발생할 C 에서 , X = {X> C}는 명시 적 값 통과 인수 또는 암시 적으로 발해야 범위 .

따라서 x 메소드를 호출 할 때 함수 리터럴의 매개 변수를 명시 으로 암시 적으로 만들 수 있습니다.

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

이것은 Play-Framework의 액션 방법 에 사용되었습니다

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())  

}

요청 매개 변수를 암시 적으로 명시 적으로 언급하지 않은 경우 다음과 같이 작성해야합니다.

def index = Action{
request =>

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

}

4

또한 위의 경우 only one유형이 인 암시 적 함수 가 있어야합니다 double => Int. 그렇지 않으면 컴파일러가 혼동되어 올바르게 컴파일되지 않습니다.

//this won't compile

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

0

스칼라의 암시 적 매우 기본적인 예.

암시 적 매개 변수 :

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

참고 : 여기 multiplier에 함수에 암시 적으로 전달됩니다 multiply. 함수 호출에 누락 된 매개 변수는 현재 범위에서 유형별로 조회됩니다. 즉, 범위에 Int 유형의 암시 적 변수가 없으면 코드가 컴파일되지 않습니다.

암시 적 변환 :

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

참고 :multiply double 값을 전달하는 함수를 호출 하면 컴파일러는 현재 범위에서 변환 암시 적 함수를 찾으려고 시도합니다 .이 함수 는 (As function accept parameter) 로 변환 Int됩니다 . 암시 적 함수 가 없으면 컴파일러는 코드를 컴파일하지 않습니다. DoublemultiplyIntconvert


0

나는 당신과 똑같은 질문을했고 실제로 몇 가지 간단한 예제로 이해하기 시작한 방법을 공유해야한다고 생각합니다 (일반적인 사용 사례 만 다루고 있습니다).

Scala를 사용하는 일반적인 사용 사례는 두 가지 implicit입니다.

  • 변수에서 사용하기
  • 함수에서 사용하기

예는 다음과 같습니다

변수에서 사용하기 . 보시다시피, implicit키워드가 마지막 매개 변수 목록에서 사용되면 가장 가까운 변수가 사용됩니다.

// 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

함수에서 사용하기 . 보시다시피 implicit함수에가 사용되면 가장 가까운 유형 변환 방법이 사용됩니다.

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!

이것이 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.