2009-10-13 2 views
4

Допустим, у меня естьсинтаксический сахар для создания объекта времени компиляции в 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:

+0

Вы хотите сериализовать функцию на удаленном компьютере? Что вы понимаете, создавая экземпляр класса «только от имени класса». Что такое класс или имя класса в этом примере? –

+0

В принципе, я хочу иметь код 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

ответ

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") 
    } 
} 
+0

Большое спасибо за это! Я полностью согласен с вами в том, что в контексте, о котором я говорил, имя класса было незначительным. Это выглядит очень перспективно! – bsdfish

2

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

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

+0

Это идея. Но что должен сказать неявный def? Все, что я могу думать, это именно то, что я определил для myFoo, и это не компиляция, а время выполнения. – bsdfish

Смежные вопросы