日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Clojure の Tagged Literals について

ClojureTagged Literalsで遊んでたところ、タグリテラル単体を評価する時ではタグとヒモ付けた関数が展開されてるみたいなんだが、他の式と組み合わせるとダメ。あるシーケンスを返すリーダーマクロなんだが、そいつにごく単純に first かけても以下のような例外出る。

CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined:

タグに紐付けられてた関数を直接使うと問題ない。ググるとどうも以下の問題っぽいなあ。

[#CLJ-955] java object reader constructor doesn't work - Clojure JIRA

サイトに書かれてる再現コード見る限り、現状 Java Interop 絡みでリーダーマクロを使うのは避けた方が良さそう。Clojure 使ってて初めて問題らしきものに遭遇したかも。1.6alpha リリースノート 見ても 1.6 では対応する気配がなさそうでちょっと残念。

そういや Java8 と Clojure の付き合いってどうなるんだろ。

[2014-02-04]
id:athos さん

初めまして。コメントありがとうございます。
ちょっとコメントでは収まりきれそうにないので、記事に追記という形で返信させて頂きます。

JavaFXlookupAll と言うメソッドがありまして、
これをラップした関数を Tagged Literal 化しようとしてました。このメソッドは CSS セレクタ書式で FXML に振られた id にマッチする Node を返すメソッドです。

メソッドをラップした lookup-all-root のソースは以下です。
Primary/scene は REPL 実行時にはインスタンス化済のものとお考え下さい。

(defn lookup-all-root [fmt]
  (.lookupAll (.getRoot Primary/scene) fmt))

lookup-all-root を Tagged Literal 化する data_readers.clj がこちら。

{ fx/s wrap-jfx.core/lookup-all-root }

Primary/scene に放り込んだ root node となる FXML 抜粋がこちら。

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="348.0" prefWidth="458.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
  <children>
    <Group id="wrapGroup"><children>
        <HBox layoutX="14.0" layoutY="14.0" prefHeight="100.0" prefWidth="360.0" spacing="6.0" style="-fx-border-color: black; -fx-border-radius: 6; -fx-padding: 6;">
        <!-- (中略) -->
        </HBox>
    </children></Group>
    <Group id="wrapGroup"><children>
        <HBox layoutX="14.0" layoutY="167.0" prefHeight="54.0" prefWidth="237.0" spacing="6.0" style="-fx-border-color: black; -fx-border-radius: 6; -fx-padding: 6;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
        <!-- (中略) -->
        </HBox>
    </children></Group>
    <Group id="wrapGroup"><children>
        <HBox layoutX="14.0" layoutY="121.0" prefHeight="36.0" prefWidth="212.0" spacing="6.0" style="-fx-border-color: black; -fx-border-radius: 6; -fx-padding: 6;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
        <!-- (中略) -->
        </HBox>
      </children></Group>
    <CheckBox id="dragMode" layoutX="8.0" layoutY="318.0" mnemonicParsing="false" text="Drag Mode" />
  </children>
</AnchorPane>

ここでは

<Group id="wrapGroup">

と Group が同一 id で 3 つ振られている事を見てもらえれば大丈夫です。


この状況で FXML の内容を Primary/scene の root に入れた後 REPL にて実行した結果がこちら。

wrap-jfx.core> #fx/s"#wrapGroup"
#{#<Group Group[id=wrapGroup]> #<Group Group[id=wrapGroup]> #<Group Group[id=wrapGroup]>}
wrap-jfx.core> (first #fx/s"#wrapGroup")
CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: [Group[id=wrapGroup], Group[id=wrapGroup], Group[id=wrapGroup]], compiling: 

wrap-jfx.core> (lookup-all-root "#wrapGroup")
#{#<Group Group[id=wrapGroup]> #<Group Group[id=wrapGroup]> #<Group Group[id=wrapGroup]>}
wrap-jfx.core> (first (lookup-all-root "#wrapGroup"))
#<Group Group[id=wrapGroup]>

このような形で、リテラル単体の評価結果は問題ないのですが、式と組み合わせると例外が発生します。