Синтаксический сахар для создания объектов во время компиляции в Scala

Допустим, у меня есть

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

Я хочу, чтобы пользователи могли быстро объявлять новые экземпляры fooTrait со своими собственными определенными телами для fooFn. В идеале я бы хотел что-то вроде

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

работать. Тем не менее, я не могу просто сделать

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

потому что это использует замыкания, и поэтому приводит к различным объектам, когда программа запускается несколько раз. Что мне действительно нужно, так это иметь возможность получить classOf объекта, возвращаемого newFoo, и затем создать его на другом компьютере. Что я делаю?

Если вас интересует сценарий использования, я пытаюсь написать оболочку Scala для Hadoop, которая позволит вам выполнить

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

Объект в середине должен быть превращен в класс, который реализует определенный интерфейс, а затем может быть создан на разных машинах (выполняя один и тот же файл JAR) только из имени класса.

Обратите внимание, что Scala правильно работает с синтаксическим сахаром, который преобразует (x: Int) => x + 5 в экземпляр Function1. Мой вопрос заключается в том, могу ли я повторить это, не взломав внутренности Scala. Если бы это был lisp (как я привык), это был бы тривиальный макрос времени компиляции ...: sniff:

13.10.2009 01:37:05
Вы хотите сериализовать функцию на удаленном компьютере? Что вы имеете в виду, когда создаете экземпляр класса "только из имени класса". Что такое класс или имя класса в этом примере?
Mitch Blevins 14.10.2009 22:34:07
По сути, я хочу иметь код Foo (Int => Int): String, который возвращает что-то. Затем на другом компьютере, на котором загружен тот же самый jar-файл, я хочу запустить Bar (s: String): FooTrait [Int] для этой строки и сделать так, чтобы Bar (Foo (fn)) возвращал объект с fn в качестве метода. , Один из способов сделать это - def Foo (obj: FooTrait [Int]) = classOf (obj) .toString, а затем сделать так, чтобы Bar создал новый экземпляр класса из имени класса, но для этого необходимо передать класс в Foo, а не лямбда
bsdfish 15.10.2009 19:28:30
2 ОТВЕТА
РЕШЕНИЕ

Вот версия, которая соответствует синтаксису того, что вы перечислили в вопросе, и сериализует / выполняет функцию anon. Обратите внимание, что это сериализует состояние объекта Function2, так что сериализованная версия может быть восстановлена ​​на другом компьютере. Просто имя класса недостаточно, как показано ниже решения.

Вы должны создать свою собственную функцию кодирования / декодирования, даже если вы просто включите свою собственную реализацию Base64 (не полагаясь на горячую точку Sun).

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

Чтобы убедиться, что имя класса недостаточно для использования функции на другом компьютере, рассмотрите приведенный ниже код, который создает 100 различных функций. Подсчитайте классы в файловой системе и сравните.

object App extends Application {
  import SHadoopImports._

  for (i <- 1 to 100) {
      IO(i + ": source") --> ((x:Int,y:Int)=>(x*i)+y) --> IO("sink")
  }
}
2
16.10.2009 05:09:57
Большое спасибо за это! Я полностью согласен с вами, что в контексте, о котором я говорил, имя класса было незначительным. Это выглядит действительно многообещающе!
bsdfish 16.10.2009 10:05:46

Подсказка: почему бы вам не попытаться создать неявный def, преобразовывающий объект FunctionN в черту, ожидаемую методом ->.

Надеюсь, вам не понадобится никакой макрос для этого!

2
13.10.2009 03:58:10
Это идея. Но что должен сказать неявный def? Все, что я могу придумать, это именно то, что я определил для myFoo, и это не компиляция, а время выполнения.
bsdfish 13.10.2009 04:43:57