How to “extract” type parameter to instantiate another class

前端 未结 2 1731
别那么骄傲
别那么骄傲 2021-01-03 08:19

The following Scala code works:

object ReducerTestMain extends App {

  type MapOutput = KeyVal[String, Int]

  def mapFun(s:String): MapOutput = KeyVal(s, 1         


        
2条回答
  •  [愿得一人]
    2021-01-03 09:06

    Just write a simple factory:

    case class RC[M <: KeyVal[_, _]](){
       def apply[K,V](f: (V,V) => V)(implicit ev: KeyVal[K,V] =:= M) = new ReducerComponent[K,V](f)
    }
    
    def plus(x: Double, y: Double) = x + y
    
    scala> RC[KeyVal[Int, Double]].apply(plus)
    res12: ReducerComponent[Int,Double] = ReducerComponent@7229d116
    
    scala> RC[KeyVal[Int, Double]]()(plus)
    res16: ReducerComponent[Int,Double] = ReducerComponent@389f65fe
    

    As you can see, ReducerComponent has appropriate type. Implicit evidence is used here to catch K and V from your M <: KeyVal[_, _].

    P.S. The version above requires to specify parameter types explicitly for your f, like (_: Double) + (_: Double). If you want to avoid this:

    case class RC[M <: KeyVal[_, _]](){
       def factory[K,V](implicit ev: KeyVal[K,V] =:= M) = new {
          def apply(f: (V,V) => V) = new ReducerComponent[K,V](f)
       }
    }
    
    scala> RC[KeyVal[Int, Double]].factory.apply(_ + _)
    res5: ReducerComponent[Int,Double] = ReducerComponent@3dc04400 
    
    
    scala> val f = RC[KeyVal[Int, Double]].factory
    f: AnyRef{def apply(f: (Double, Double) => Double): ReducerComponent[Int,Double]} = RC$$anon$1@19388ff6
    
    scala> f(_ + _)
    res13: ReducerComponent[Int,Double] = ReducerComponent@24d8ae83
    

    Update. If you want to generelize keyval - use type function:

    type KV[K,V] = KeyVal[K,V] //may be anything, may implement `type KV[K,V]` from some supertrait
    
    case class RC[M <: KV[_, _]](){   
      def factory[K,V](implicit ev: KV[K,V] =:= M) = new {
        def apply(f: (V,V) => V) = new ReducerComponent[K,V](f)
      }
    }
    

    But keep in mind that apply from your question still takes KeyVal[K,V].

    You can also pass KV into some class:

    class Builder[KV[_,_]] {
      case class RC[M <: KV[_, _]](){   
        def factory[K,V](implicit ev: KV[K,V] =:= M) = new {
          def apply(f: (V,V) => V) = new ReducerComponent[K,V](f)
        }
      }
    }
    
    scala> val b = new Builder[KeyVal]
    scala> val f = b.RC[KeyVal[Int, Double]].factory
    scala> f(_ + _)
    res2: ReducerComponent[Int,Double] = ReducerComponent@54d9c993
    

提交回复
热议问题