ページ

2025年3月9日日曜日

clojureで関数内変数との競合を回避してnamespaceの変数を呼び出す方法


背景

プログラミング言語clojureで「namespaceで定義した変数」と「関数でキーワードで受け取る引数」の競合が発生してnamespace側の変数の呼び方が分からなくてしばらく困りました。
いくつかの解決方法が分かったので、関連情報と共に備忘録として記事に残します。

動作確認した環境

clojure 1.11.1
shadow-cljs 2.28.20

問題が発生する記述

下記の記述だとファイル冒頭で定義したsome-valが関数内で呼べません。

(ns some.file)

(def some-val 5)

(defn soem-fn [{:keys [some-val]}]
(println (if (nil? some-val) some-val some-val)))

解決方法1: @#'を使う

@#'を使うことで、他のプログラミング言語c++ の :: のように関数内の変数ではなくnamespaceで定義した変数を呼べます。
(def some-val 5)

(defn soem-fn [{:keys [some-val]}]
(println (if (nil? some-val) some-val @#'some-val)))

@はderef#'はvarの略記で、 @#'some-val は (deref (var some-val) とも書けます。
varでsome-valという名前のポインタを作り、derefでその名前で参照できる値を取得しているようです。

athosさんに教えていただきました。


@#'で呼び出す変数が無ければclojure-cliもshadow-cljsも静的解析ツールのclj-kondo教えてくれたので、@#'を使うにあたって特別な注意は必要無さそうでした。




参考
「clojure @#'」で検索して見つかったサイトです。
1行ずつ説明があります。
Clojureスタイルガイド
Contrib How-To

解決方法2: ファイルのnamespace名を付けて変数を呼ぶ

他のファイルで呼び出す場合と同様にnamespaceと/を組み合わせて呼べば関数内変数と名称が被らず呼び出せます。
(ns some.file)

(def some-val 5)

(defn soem-fn [{:keys [some-val]}]
(println (if (nil? some-val) some-val some.file/some-val)))

解決方法3: 名前が被らないprivate変数を定義して呼ぶ

別の名前で定義した変数を通せば所望の値を呼べます。
(ns some.file)

(def some-val 5)
(def ^:private this-some-val some-val)

(defn soem-fn [{:keys [some-val]}]
(println (if (nil? some-val) some-val this-some-val)))

おわり

@#'を使えばプログラミング言語c++の :: のようにnamespace内の要素を呼び出せると分かりました。
自力でその記述に辿り着くのは困難だったので、情報共有がありがたかったです。

参考

情報共有があったツイートです。
問題提起
https://x.com/asukiaaa/status/1898341742900092976
情報共有
https://x.com/athos0220/status/1898385131708228093
解説
https://x.com/athos0220/status/1898622930688393475

@#'の解説が1行だけあるサイトです。
Clojureスタイルガイド
Contrib How-To

@#'の別表記です。
deref
var

0 件のコメント :

コメントを投稿