Clojureの関数で渡されたものと同じ型のコレクションを返すには？


例えば次のような関数を定義したとして、渡されたコレクションと同じ型を返す良い方法はないでしょうか？
ただし、評価値の順序がおかしくならないようにお願いします。

[code]

    (defn rotate [n coll]
      (let [at (if (neg? n) (Math/abs n) (- (count coll) n))
           [left right] (split-at at coll)]
        (concat right left)))
    
[/code]

こんなやりかたを考えてみましたが、ごちゃごちゃして気持ち悪いですよね。 何かシンプルな方法はないでしょうか？

[code]

    (defmulti vomit (fn [coll]
                      (if (list? coll) :list (if (vector? coll) :vector))) :default nil)
    (defmethod vomit :list [coll] (partial apply list))
    (defmethod vomit :vector [coll] (partial into (empty coll)))
    (defmethod vomit nil [coll] identity)
    
    (defn rotate [n coll]
      (let [at (if (neg? n) (Math/abs n) (- (count coll) n))
            [left right] (split-at at coll)]
        ((vomit coll) (concat right left))))
    
[/code]




以下の様な書き方はいかがでしょうか。

[code]

    (defn lisptype [x]
      (cond
        (list? x) list
        (vector? x) vector
        (map? x) hash-map
        (string? x) (fn [& x] (clojure.string/join x))
        true list))
    
    (defn rotate [n coll]
      (let [at (if (neg? n) (Math/abs n) (- (count coll) n))
           [left right] (split-at at coll)]
      (apply (lisptype coll) (concat right left))))
    
[/code]

※ map を渡すと妙な感じになりますが…

clojure の `type function` が Java の型ではなくて clojure 本来の型(list や
vector)を返してくれればもう少し簡潔に書くことができるのですが…。

[code]

    (apply (type coll) (concat right left))
    
[/code]

追記:

rizaudo さんの回答を参考にさせていただいて、lisptype function を書き換えました。map に関しては array-map
のみの対応で、set に関しては [Are there sets that keep the insertion
order?](https://groups.google.com/forum/#!topic/clojure/vYND1CtKf2M) に記載されている
order-preserving-set function を拝借しました(少し長いのでここには載せていません)。

[code]

    (defn lisptype [x]
      (cond
        (list? x) list
        (vector? x) vector
        (set? x) order-preserving-set
        (map? x) (fn [& x] (apply array-map (flatten x)))
        (string? x) (fn [& x] (clojure.string/join x))
        :else list))
    
[/code]

言わずもがなですが、rotate function に渡す set 型データは order-preserving-set function
を使って作成する必要があります。




以下の様な書き方はいかがでしょうか。

[code]

    (defn lisptype [x]
      (cond
        (list? x) list
        (vector? x) vector
        (map? x) hash-map
        (string? x) (fn [& x] (clojure.string/join x))
        true list))
    
    (defn rotate [n coll]
      (let [at (if (neg? n) (Math/abs n) (- (count coll) n))
           [left right] (split-at at coll)]
      (apply (lisptype coll) (concat right left))))
    
[/code]

※ map を渡すと妙な感じになりますが…

clojure の `type function` が Java の型ではなくて clojure 本来の型(list や
vector)を返してくれればもう少し簡潔に書くことができるのですが…。

[code]

    (apply (type coll) (concat right left))
    
[/code]

追記:

rizaudo さんの回答を参考にさせていただいて、lisptype function を書き換えました。map に関しては array-map
のみの対応で、set に関しては [Are there sets that keep the insertion
order?](https://groups.google.com/forum/#!topic/clojure/vYND1CtKf2M) に記載されている
order-preserving-set function を拝借しました(少し長いのでここには載せていません)。

[code]

    (defn lisptype [x]
      (cond
        (list? x) list
        (vector? x) vector
        (set? x) order-preserving-set
        (map? x) (fn [& x] (apply array-map (flatten x)))
        (string? x) (fn [& x] (clojure.string/join x))
        :else list))
    
[/code]

言わずもがなですが、rotate function に渡す set 型データは order-preserving-set function
を使って作成する必要があります。

