かみつかさと申します。
私も以前から気になっていたのでこの機会に調べてみました。
なぜそうなっているのかはわからなかったのですが
確かに Compiler.java の MapExpr.parse において
{:a 1 :b 2 :c 3} のような全てのkeyとvalueが定数のMapは
PersistentHashMapに詰めかえられていました。
他の例についても少し追ってみましたので書いてみます。誤りはご指摘ください。
まずClojureのコンパイラはざっくりと次のような順序で処理します。
1. read
文字列を読み込んで form と呼ばれる Object を返します。
例えば "abc" は String、(:a :b) は ISeq に変換されます。
2. analyze
form Object を Expr へ変換します。
例えば (println 123) というISeqのformは InvokeExprに変換されます。
マクロ展開もここで実施されます。
3. eval
Expr を評価して値を得ます。
それぞれの例がどのように変化するかですが…
user=> {:a 1 :b 2 :c 3} の場合
1. read
PersistentArrayMapに変換。
2. analyze
key, val全てが定数のMapの場合 PersistentHashMap に詰めなおされた後にConstantExprに包まれる。
ここで順序が崩れる。
3. eval
再度PersistentArrayMapに詰められる。
user=> (array-map :a 1 :b 2 :c 3) の場合
1. read
ISeq に変換。
2. analyze
InvokeExpr に変換。
3. eval
array-map に引数(:a 1 :b 2 :c 3)が適用されて PersistentArrayMap が得られる。
user=> (read-string "{:a 1 :b 2 :c 3}")
1. read
ISeq に変換。
2. analyze
InvokeExpr に変換。
3. eval
read-stringに引数("{:a 1 :b 2 :c 3}")が適用される。
ただしread-stringにおいては read が呼ばれるのみ。そのため PersistentArrayMap が返却されて終了。
リテラル表記とそれ以外のケースではanalyzeに渡されるformに差があり
IPersistentMapのanalyzeにおいては全key/valが定数のMapが特別扱いされて
PersistentHashMapに詰め直され、その結果、順序が崩れてしまうようです。