복붙노트

[SCALA] 어떻게 동적 작업을 입력 기능과 사용 방법에?

SCALA

어떻게 동적 작업을 입력 기능과 사용 방법에?

나는 동적으로 스칼라 동적 타이핑을 할 어떻게 든 가능하다고 들었습니다. 하지만 그와 같은 방법이나 작동 볼 수있는 방법을 상상할 수 없다.

나는 하나의 특성 동적에서 상속 할 수 있다는 것을 발견

class DynImpl extends Dynamic

API는 하나 같이 사용할 수 있다고 말한다 :

하지만 그것을 밖으로 시도 할 때 작동하지 않습니다 :

scala> (new DynImpl).method("blah")
<console>:17: error: value applyDynamic is not a member of DynImpl
error after rewriting to new DynImpl().<applyDynamic: error>("method")
possible cause: maybe a wrong Dynamic method signature?
              (new DynImpl).method("blah")
               ^

소스에보고 후, 그것은이 특성이 완전히 비어 있음을 밝혀 때문에, 완전히 논리적이다. 이 applyDynamic가 정의 된 방법은없고 나 자신에 의해 그것을 구현하는 방법을 상상할 수 없다.

누군가가 내가 작동 할 수 있도록해야 할 일을 저를 게재 할 수 있습니까?

해결법

  1. ==============================

    1.Scalas 유형의 동적 당신이 존재하거나 다른 단어에서 동적 언어로 "방법이없는"의 복제는하지 않는 객체의 메소드를 호출 할 수 있습니다.

    Scalas 유형의 동적 당신이 존재하거나 다른 단어에서 동적 언어로 "방법이없는"의 복제는하지 않는 객체의 메소드를 호출 할 수 있습니다.

    그것은 단지 마커 인터페이스, scala.Dynamic 어떤 멤버가없는, 올 - 구체적인 구현은 채워진 컴파일러입니다. Scalas 문자열 보간 기능에 관해서는 생성 된 구현을 설명하는 잘 정의 된 규칙이 있습니다. 사실, 하나는 네 가지 방법을 구현할 수 있습니다 :

    이 동적 확장하는 클래스를 작성하고이 방법을 구현하기 위해 충분하다 다음 방법 중 하나를 사용합니다 :

    class DynImpl extends Dynamic {
      // method implementations here
    }
    

    또 하나는 추가해야합니다

    import scala.language.dynamics
    

    또는 컴파일러 옵션 -language을 설정 역학 기능이 기본적으로 숨겨져 있기 때문이다.

    SELECTDYNAMIC 구현하는 가장 쉬운 일입니다. 컴파일러는 따라서이 방법은 문자열을 기대 인수 목록이 필요가있다, foo.selectDynamic ( "바")에 foo.bar의 전화를 번역 :

    class DynImpl extends Dynamic {
      def selectDynamic(name: String) = name
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@6040af64
    
    scala> d.foo
    res37: String = foo
    
    scala> d.bar
    res38: String = bar
    
    scala> d.selectDynamic("foo")
    res54: String = foo
    

    하나시피 명시 적 동적 메소드를 호출 할 수있다.

    updateDynamic이 방법은 단위를 반환 할 필요가있는 값을 업데이트하는 데 사용되기 때문이다. 또한, 업데이트 할 수있는 필드의 이름과 값은 컴파일러에 의해 서로 다른 인수 목록에 전달됩니다 :

    class DynImpl extends Dynamic {
    
      var map = Map.empty[String, Any]
    
      def selectDynamic(name: String) =
        map get name getOrElse sys.error("method not found")
    
      def updateDynamic(name: String)(value: Any) {
        map += name -> value
      }
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@7711a38f
    
    scala> d.foo
    java.lang.RuntimeException: method not found
    
    scala> d.foo = 10
    d.foo: Any = 10
    
    scala> d.foo
    res56: Any = 10
    

    코드는 예상대로 작동합니다 - 코드를 런타임에 메서드를 추가 할 수 있습니다. 다른 측면에서,이 코드는 더 이상 형태 보증되지 않고, 메소드가 불려 갔을 경우 이것은뿐만 아니라 런타임에 처리해야 존재하지 않습니다. 런타임시 호출해야합니다 방법을 만들 수 없기 때문에 또한이 코드는 동적 언어로 유용하지 않습니다. 이것은 우리가 같은 일을 할 수 없다는 것을 의미합니다

    val name = "foo"
    d.$name
    

    라. $ 이름은 런타임에 d.foo로 변환 될 것이다. 심지어 동적 언어이 위험한 기능이기 때문에 그러나 이것은 나쁜 없습니다.

    여기에서 주목해야 할 또 다른 것은, updateDynamic가 SELECTDYNAMIC와 함께 구현 될 필요가 있다는 점이다. 우리가 이렇게하지 않으면 우리는 컴파일 오류가 발생합니다 -이 규칙은 같은 이름의 게터가있는 경우에만 작동하는 세터의 구현과 유사하다.

    applyDynamic에 의해 제공됩니다 인수 메소드를 호출 할 수있는 기능 :

    class DynImpl extends Dynamic {
      def applyDynamic(name: String)(args: Any*) =
        s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@766bd19d
    
    scala> d.ints(1, 2, 3)
    res68: String = method 'ints' called with arguments '1', '2', '3'
    
    scala> d.foo()
    res69: String = method 'foo' called with arguments ''
    
    scala> d.foo
    <console>:19: error: value selectDynamic is not a member of DynImpl
    

    방법 및 인수의 이름은 다시 다른 매개 변수 목록에 분리된다. 우리는 우리가 SELECTDYNAMIC을 구현하는 데 필요한 모든 괄호없이 메소드를 호출 할 경우, 우리는 우리가 원하는 경우 인수의 임의의 숫자와 임의의 메소드를 호출 할 수 있지만.

    힌트 : applyDynamic와 적용 - 구문을 사용하는 것도 가능합니다 :

    scala> d(5)
    res1: String = method 'apply' called with arguments '5'
    

    마지막으로 가능한 방법은 우리가 원하는 경우 우리의 인수의 이름을 우리에게 수 있습니다 :

    class DynImpl extends Dynamic {
    
      def applyDynamicNamed(name: String)(args: (String, Any)*) =
        s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@123810d1
    
    scala> d.ints(i1 = 1, i2 = 2, 3)
    res73: String = method 'ints' called with arguments '(i1,1)', '(i2,2)', '(,3)'
    

    메소드 서명 차이는 applyDynamicNamed 폼의 튜플 A는 임의의 타입 (문자열, A)을 예상하는 것이다.

    위의 방법들은 모두 매개 변수는 매개 변수가 될 수 있다는 공통점이 :

    class DynImpl extends Dynamic {
    
      import reflect.runtime.universe._
    
      def applyDynamic[A : TypeTag](name: String)(args: A*): A = name match {
        case "sum" if typeOf[A] =:= typeOf[Int] =>
          args.asInstanceOf[Seq[Int]].sum.asInstanceOf[A]
        case "concat" if typeOf[A] =:= typeOf[String] =>
          args.mkString.asInstanceOf[A]
      }
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@5d98e533
    
    scala> d.sum(1, 2, 3)
    res0: Int = 6
    
    scala> d.concat("a", "b", "c")
    res1: String = abc
    

    우리는 우리가 쉽게 인수의 유형을 확인할 수 있습니다 바인딩 TypeTag 컨텍스트를 추가하는 경우 - 다행히, 암시 인수를 추가 할 수있다. 그리고 가장 좋은 점은 심지어 반환 형식이 올바른지입니다 - 우리는 어떤 캐스트를 추가했다하더라도.

    이러한 결함 주위에 방법을 찾을 수있는 방법이 없습니다 때 스칼라 스칼라하지 않을 것입니다. 우리의 경우 우리는 캐스트를 피하기 위해 형 클래스를 사용할 수 있습니다 :

    object DynTypes {
      sealed abstract class DynType[A] {
        def exec(as: A*): A
      }
    
      implicit object SumType extends DynType[Int] {
        def exec(as: Int*): Int = as.sum
      }
    
      implicit object ConcatType extends DynType[String] {
        def exec(as: String*): String = as.mkString
      }
    }
    
    class DynImpl extends Dynamic {
    
      import reflect.runtime.universe._
      import DynTypes._
    
      def applyDynamic[A : TypeTag : DynType](name: String)(args: A*): A = name match {
        case "sum" if typeOf[A] =:= typeOf[Int] =>
          implicitly[DynType[A]].exec(args: _*)
        case "concat" if typeOf[A] =:= typeOf[String] =>
          implicitly[DynType[A]].exec(args: _*)
      }
    
    }
    

    구현이 그 좋은 보이지 않지만, 그 힘은 의문을 제기 할 수 없습니다

    scala> val d = new DynImpl
    d: DynImpl = DynImpl@24a519a2
    
    scala> d.sum(1, 2, 3)
    res89: Int = 6
    
    scala> d.concat("a", "b", "c")
    res90: String = abc
    

    모두의 상단에, 매크로 동적 결합하는 것도 가능합니다 :

    class DynImpl extends Dynamic {
      import language.experimental.macros
    
      def applyDynamic[A](name: String)(args: A*): A = macro DynImpl.applyDynamic[A]
    }
    object DynImpl {
      import reflect.macros.Context
      import DynTypes._
    
      def applyDynamic[A : c.WeakTypeTag](c: Context)(name: c.Expr[String])(args: c.Expr[A]*) = {
        import c.universe._
    
        val Literal(Constant(defName: String)) = name.tree
    
        val res = defName match {
          case "sum" if weakTypeOf[A] =:= weakTypeOf[Int] =>
            val seq = args map(_.tree) map { case Literal(Constant(c: Int)) => c }
            implicitly[DynType[Int]].exec(seq: _*)
          case "concat" if weakTypeOf[A] =:= weakTypeOf[String] =>
            val seq = args map(_.tree) map { case Literal(Constant(c: String)) => c }
            implicitly[DynType[String]].exec(seq: _*)
          case _ =>
            val seq = args map(_.tree) map { case Literal(Constant(c)) => c }
            c.abort(c.enclosingPosition, s"method '$defName' with args ${seq.mkString("'", "', '", "'")} doesn't exist")
        }
        c.Expr(Literal(Constant(res)))
      }
    }
    
    scala> val d = new DynImpl
    d: DynImpl = DynImpl@c487600
    
    scala> d.sum(1, 2, 3)
    res0: Int = 6
    
    scala> d.concat("a", "b", "c")
    res1: String = abc
    
    scala> d.noexist("a", "b", "c")
    <console>:11: error: method 'noexist' with args 'a', 'b', 'c' doesn't exist
                  d.noexist("a", "b", "c")
                           ^
    

    매크로는 우리가 다시 모든 시간 보장을 컴파일 제공하고 위의 경우에서 유용하지 않습니다 동안, 어쩌면 약간의 스칼라 DSL을 위해 매우 유용 할 수 있습니다.

    당신이 동적에 대한 더 많은 정보를 얻고 싶다면 몇 가지 더 많은 자원이있다 :

  2. from https://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-it by cc-by-sa and MIT license