在介紹 PigPen 的文章中,做者在 Future Work 一節中提到,咱們在 PigPen 中不能調用本地聲明的代碼。例如一下代碼在最後生成的 Pig 腳本中是執行不了的,會報找不到符號 foo
的錯誤:html
(ns test.core (:require [pigpen.core :as pig])) (defn foo [x] …) (pig/map foo)
這樣就只能把全部 foo
函數要作的事情所有寫在 pig/map
後面。若是 foo
要作的事情不少,代碼很長,那麼寫出來的代碼將很是難看。git
PigPen 不光不支持本地代碼的調用,還不支持 namespace 的引用(https://groups.google.com/forum/#!msg/pigpen-support/-Kd06UfzxEU/vYEAZvmZLFcJ)。github
這些在任何編程語言中都看似很天然的功能在初期的 PigPen 中都不支持。其實這裏的每個本地代碼的調用或者其餘 namespace 中函數的調用在最後寫成 Pig 腳本後都是一個 UDF,而 PigPen 的目的之一也就是要取代 Pig 腳本和 UDF混寫的方式,因此應該會有解決的辦法,但沒時間(也懶)去扒源代碼去想辦法處理這種狀況,因而到 GitHub 上找到主要的貢獻者 Matt Bossenbroek,向他發郵件請教。編程
Matt 指出若是要用調用本地代碼須要這樣作:編程語言
(ns test.core (:require [pigpen.core :as pig])) (defn foo [x] …) (pig/map (do (require 'test.core) foo))
我嘗試了一下,好像並不能達到預期的效果。函數
因而我試着改變一下生成的 Pig 腳本,看能不能運行起來。反正最後部署到集羣上運行的時候也是用的生成的 Pig 腳本文件。在生成的 Pig 腳本中,全部的 Clojure 代碼都被 pigpen.PigPenFn*
一組類(PigPenFnBoolean
,PigPenFnString
,PigPenFnTuple
,PigPenFnDataBag
,PigPenFnDataByteArray
)包裝成 UDF 插入到最終生成的 Pig 腳本中,不一樣的後綴返回不一樣的 Pig 基本類型的值。ui
在生成的 Pig 腳本中,這些類的第一個參數都是 '(clojure.core/require (quote [pigpen.pig]))'
,看一下代碼知道這個參數會被當作 Clojure 代碼讀入來作初始化用,因此應該在這裏加上要包含的 namespace。試了一些果真能夠運行了。再看一下代碼發現要經過 PigPen 的方法來添加這個 require
也很差弄,就打算先用 PigPen 生成 Pig 腳本,而後在這裏加上另外要包含的 namespace(寫一個腳本往每一個這樣的類裏面加上 namespace 也不會太麻煩)。google
同時也順帶問一下 Matt 怎麼利用 PigPen 來作,Matt 說正在考慮用什麼樣的方式來支持引用 namespace。結果在大年初二早上收到郵件說新版本的 PigPen([com.netflix.pigpen/pigpen "0.1.4"]) 能夠支持了,並給出了一個例子:spa
(ns pigpen-demo.core (:require [pigpen.core :as pig] [clojure.string :as str])) (defn square [x] (* x x)) (defn my-query [] (->> (pig/return [1 2 3]) (pig/map square) (pig/map (fn [x] (square x))) (pig/into []) (pig/map #(str/join "," %)) (pig/dump)))
可是必定要把整個項目打包成一個 uberjar 分發到集羣上去。這樣基本上能夠把 PigPen 用到個人實驗性的工做中去了(production 環境不容許亂用新東西)。.net