Mungkin di clojure

Mencoba menulis fungsi tersusun di clojure yang keluar pada nilai nil pertama, (misalnya sesuatu yang akan Anda lakukan dengan merangkai Maybes bersama-sama di haskell) dengan yang berikut:

(defn wrap [f] (fn [x] (if (nil? x) nil (f x))))

(defn maybe [arg & functs] ( (comp (reverse (map wrap functs))) arg))

Agar saya mendapatkan, mis.

(defn f1 [x] (+ x 1))

(maybe 1 f1 f1 ) => 3

(maybe nil f1 f1) => nil

Yang sayangnya memberi saya ini: ClassCastException clojure.lang.PersistentList tidak dapat dilemparkan ke clojure.lang.IFn pengguna/mungkin (NO_SOURCE_FILE:1)

Dapatkah seseorang memberikan bantuan tentang kesalahan saya di sini? Apa cara idiomatis untuk melakukan ini?


person Steve B.    schedule 14.05.2015    source sumber
comment
apa yang dilakukan wrap (dan x (f x )) tidak?   -  person RedDeckWins    schedule 14.05.2015


Jawaban (4)


Cara idiomatis untuk melakukan ini adalah dengan menggunakan some->. Lihat dokumentasi ini makro untuk lebih jelasnya.

Tentu saja, jangan biarkan hal itu menghentikan Anda untuk membuatnya sendiri!

person WolfeFan    schedule 14.05.2015
comment
Perhatikan bahwa penggunaan yang dihasilkan sedikit berbeda; makro some-› dengan baik mendorong hasil Anda secara implisit melalui formulir dan tidak dapat dikomposisi sedangkan fungsi mungkin memerlukan fungsi sebenarnya tetapi dapat dikomposisi. misalnya (some->> [1] (map inc)) tapi (maybe [1] #(map inc %)) - person Dax Fohl; 14.05.2015
comment
@DaxFohl maksud Anda itu tidak dapat disusun karena ini makro? Apa perbedaan antara kedua cuplikan di komentar Anda? - person muhuk; 15.05.2015
comment
@muhuk Ya, ini tidak dapat disusun karena ini makro; jika Anda ingin membuat daftar fungsi saat runtime untuk dieksekusi dengan cara ini, Anda tidak dapat melakukannya dengan some->. Perbedaannya adalah (map inc) lebih pendek, tetapi ini bukan ekspresi yang akan dikompilasi di luar makro some->. - person Dax Fohl; 15.05.2015
comment
Mengerti. Namun hal ini tidak menjadikan some-> dirinya sendiri tidak dapat dikomposisi. Anda juga masih bisa (def f #(map inc %)) dan kemudian (some->> [1] (f)) jika harus. - person muhuk; 15.05.2015
comment
@muhuk bilang kamu punya (defn runall [arg & functs] ((comp (reverse functs)) arg)), hanya maybe tanpa wrap. Anda dapat memilih di antara keduanya saat runtime (let [myfunc (if whatever maybe runall)] (myfunc input f1 f2 f3)). Tidak dapat melakukan ini dengan some->. Itulah yang dimaksud orang ketika mereka mengatakan makro tidak dapat dikomposisi. - person Dax Fohl; 15.05.2015

comp mengharapkan setiap fungsi sebagai argumen individual, tetapi Anda meneruskannya ke daftar fungsi sebagai argumen tunggal. Untuk menyiasatinya, gunakan apply.

(defn maybe [arg & functs] ( (apply comp (reverse (map wrap functs))) arg))
person Dax Fohl    schedule 14.05.2015

Selalu ada namespace clojure.algo.monads dengan monad maybe-m:

(with-monad maybe-m
  (defn adder [x]
    (let [f (fn [x] (+ x 1))]
      (domonad
        [a x
         b (f a)
         c (f b)]
       c))))

(adder 1)
=> 3 

(adder nil)
=> nil

Memang benar, ini mungkin sedikit berlebihan untuk kebutuhan Anda

person Mark Fisher    schedule 15.05.2015

Saya tahu ini telah dijawab, dan saya sudah memasangnya sendiri, tetapi saya pikir saya akan menambahkan yang berikut ini karena saya telah memainkannya menggunakan monad lagi, dan ini sepertinya pertanyaan yang bagus untuk ditentang.

Membaca artikel tentang threading monads ini, saya bisa datang lakukan yang berikut ini dengan memperluas makro m-> yang ditentukan dalam artikel untuk membuat mungkin monad berulir untuk penggunaan yang lebih sederhana. TBH, ini tidak lebih sederhana daripada hanya menggunakan some-> tetapi ini untuk keingintahuan pribadi.

Oke, sebagai permulaan, ada satu kode pelat ketel yang harus didefinisikan, berikut (kalau-kalau artikelnya hilang) adalah definisi monad berulir Giles:

(defn bind-monadic-expr-into-form [insert form]
  (list 'm-bind insert
        (if (seq? form)
          `(fn [bound#] (~(first form) bound# ~@(rest form)))
          `(fn [bound#] (~form bound#)))))

(defmacro m->
  ([m x]
   `(with-monad ~m ~x))
  ([m x form]
   `(with-monad ~m
                ~(bind-monadic-expr-into-form x form)))
  ([m x form & more]
   `(m-> ~m (m-> ~m ~x ~form) ~@more)))

Sekarang dengan ini, Anda dapat mendefinisikan makro berulir sebagai

(defmacro maybe->
  ([x] `(m-> ~maybe-m ~x))
  ([x form] `(m-> ~maybe-m ~x ~form))
  ([x form & more] `(maybe-> (maybe-> ~x ~form) ~@more)))

Dan gunakan seperti:

(maybe-> 1 inc)
=> 2

(maybe-> [1 2] (#(map inc %)))
=> (2 3)

(defn f1 [x] (+ 1 x))
(maybe-> 1 f1 f1)
=> 3

(maybe-> 1 f1 ((constantly nil)) f1)
=> nil

(maybe-> {:a 1 :b 2} :c inc)
=> nil

Sama sekali tidak ada keuntungan menggunakan ini dibandingkan some-> dalam konteks ini, tetapi monad m-> memang menambahkan beberapa kemampuan menarik untuk dapat membuat makro fail-> seperti pada artikel yang saya tautkan, yang menawarkan lebih dari sekadar "nihil" sebagai imbalannya, memberi Anda kemampuan untuk membedakan alasan kegagalan.

person Mark Fisher    schedule 18.05.2015