简体   繁体   English

用于Scala中编译时对象创建的语法糖

[英]Syntactic sugar for compile-time object creation in Scala

Lets say I have 可以说我有

trait fooTrait[T] {
  def fooFn(x: T, y: T) : T 
}

I want to enable users to quickly declare new instances of fooTrait with their own defined bodies for fooFn. 我希望用户能够使用自己定义的fooFn主体快速声明fooTrait的新实例。 Ideally, I'd want something like 理想情况下,我想要类似的东西

val myFoo : fooTrait[T] = newFoo((x:T, y:T) => x+y) 

to work. 上班。 However, I can't just do 但是,我不能这样做

def newFoo[T](f: (x:T, y:T) => T) = new fooTrait[T] { def fooFn(x:T, y:T):T = f(x,y); }

because this uses closures, and so results in different objects when the program is run multiple times. 因为它使用闭包,因此在程序多次运行时会产生不同的对象。 What I really need is to be able to get the classOf of the object returned by newFoo and then have that be constructable on a different machine. 真正需要的是能够获取newFoo返回的对象的classOf,然后可以在不同的机器上构造它。 What do I do? 我该怎么办?

If you're interested in the use case, I'm trying to write a Scala wrapper for Hadoop that allows you to execute 如果您对用例感兴趣,我正在尝试为Hadoop编写一个Scala包装器,允许您执行

IO("Data") --> ((x: Int, y: Int) => (x, x+y)) --> IO("Out")

The thing in the middle needs to be turned into a class that implements a particular interface and can then be instantiated on different machines (executing the same jar file) from just the class name. 中间的东西需要变成一个实现特定接口的类,然后可以从类名实例化到不同的机器上(执行相同的jar文件)。

Note that Scala does the right thing with the syntactic sugar that converts (x:Int) => x+5 to an instance of Function1. 请注意,Scala使用将(x:Int)=> x + 5转换为Function1实例的语法糖做正确的事情。 My question is whether I can replicate this without hacking the Scala internals. 我的问题是我是否可以在不破坏Scala内部的情况下复制此内容。 If this was lisp (as I'm used to), this would be a trivial compile-time macro ... :sniff: 如果这是lisp(我已经习惯了),这将是一个简单的编译时宏......:嗅探:

Quick suggestion: why don't you try to create an implicit def transforming FunctionN object to the trait expected by the --> method. 快速建议:为什么不尝试创建一个隐式def转换FunctionN对象到 - >方法所期望的特征。

I do hope you won't have to use any macro for this! 我希望你不必为此使用任何宏!

Here's a version that matches the syntax of what you list in the question and serializes/executes the anon-function. 这是一个与您在问题中列出的语法相匹配并序列化/执行anon-function的版本。 Note that this serializes the state of the Function2 object so that the serialized version can be restored on another machine. 请注意,这会序列化Function2对象的状态,以便可以在另一台计算机上还原序列化版本。 Just the classname is insufficient, as illustrated below the solution. 只是类名不足,如解决方案下面所示。

You should make your own encode/decode function, if even to just include your own Base64 implementation (not to rely on Sun's Hotspot). 您应该创建自己的编码/解码功能,即使只包含您自己的Base64实现(不依赖于Sun的Hotspot)。

object SHadoopImports {
    import java.io._

    implicit def functionToFooString[T](f:(T,T)=>T) = {
        val baos = new ByteArrayOutputStream()
        val oo = new ObjectOutputStream(baos)
        oo.writeObject(f)
        new sun.misc.BASE64Encoder().encode(baos.toByteArray())
    }

    implicit def stringToFun(s: String) = {
        val decoder = new sun.misc.BASE64Decoder();
        val bais = new ByteArrayInputStream(decoder.decodeBuffer(s))
        val oi = new ObjectInputStream(bais)  
        val f = oi.readObject()
        new {
            def fun[T](x:T, y:T): T = f.asInstanceOf[Function2[T,T,T]](x,y)
        }
    }
}

// I don't really know what this is supposed to do
// just supporting the given syntax
case class IO(src: String) {
    import SHadoopImports._
    def -->(s: String) = new {
        def -->(to: IO) = {
            val IO(snk) = to
            println("From: " + src)
            println("Applying (4,5): " + s.fun(4,5))
            println("To: " + snk)
        }
    }
}

object App extends Application {
  import SHadoopImports._

  IO("MySource") --> ((x:Int,y:Int)=>x+y) --> IO("MySink")
  println
  IO("Here") --> ((x:Int,y:Int)=>x*y+y) --> IO("There")
}

/*
From: MySource
Applying (4,5): 9
To: MySink

From: Here
Applying (4,5): 25
To: There
*/

To convince yourself that the classname is insufficient to use the function on another machine, consider the code below which creates 100 different functions. 为了说服自己,类名不足以在另一台机器上使用该函数,请考虑下面的代码创建100个不同的函数。 Count the classes on the filesystem and compare. 计算文件系统上的类并进行比较。

object App extends Application {
  import SHadoopImports._

  for (i <- 1 to 100) {
      IO(i + ": source") --> ((x:Int,y:Int)=>(x*i)+y) --> IO("sink")
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM