ちょっと色々検索してて目に入った中にタイトルのようなことをしようとしている記事が幾つかあって、何故か eval
に行き着いてしまっていたりやたら複雑なことをしていたりしていたのでちょっと正しい方法とか書いておこうと思います。ちなみにどの言語でもそうだと思いますが eval
は最終兵器であり基本的には使うべきではないものだと認識しています(だいたいやりたいことするための API は揃っている)。
同一ネームスペースの関数を実行する
(ns example.core)
(defn foo []
"This is 'example.core/foo' function")
こういう foo
関数があった場合に同じネームスペースから文字列を用いて実行する場合は次のようにします。
((resolve (symbol "foo"))) ;;=> This is 'example.core/foo' function
違うネームスペースの関数を実行する
(ns example.other)
(defn bar []
"This is 'example.other/bar' function.")
こういう関数が定義してあるネームスペースがあってこれを example.core
から呼び出したいときはこう書きます。
(let [s "example.other/bar"
k (keyword s)
ns-sym (symbol (namespace k))
name-sym (symbol (name k))]
((ns-resolve ns-sym name-sym))) ;;=> This is 'example.other/bar' function.
Java のメソッドを実行する
(let [c "java.lang.String"
m "valueOf"
args [200]]
(clojure.lang.Reflector/invokeStaticMethod c m (into-array args))) ;;=> 200
clojure.lang.Reflector
には他にも invokeInstanceMethod
など欲しくなりそうなものがあるので困ったら覗いてみるといいかもしれません。
まとめ
Clojure にはだいたい「やりたい」と思うことを簡単にやる方法が既に用意されていることが多いです。ただ、それが日本語になっていないというだけで今回の問題も clojure function call from string
などと検索すればだいたい出てきます。
元ネタ
途中で言及されている if
を呼び出すのはちょっと正攻法じゃ出来ない気がしてます(スペシャルフォームは流石に Compiler レベルまで降りるか eval
じゃないと無理じゃないかなぁ/単純に構文的な何かを作りたいならマクロで対処出来るんですけどね)。
流石にここまでして関数を動的に呼び出す人はいないと思いますが…。