2013-04-17 27 views
11

Tôi gặp phải, với tôi, hành vi hơi ngạc nhiên dường như liên quan đến hồ sơ clojure.Hành vi đáng ngạc nhiên liên quan đến hồ sơ, giao thức và biên soạn

Cách cài đặt như sau:

  1. Một namespace định nghĩa một loại kỷ lục:

    (ns defrecordissue.arecord) 
    
    (defrecord ARecord []) 
    
  2. namespace Một định nghĩa một giao thức, và mở rộng nó vào loại kỷ lục quy định tại 1:

    (ns defrecordissue.aprotocol 
        (:require [defrecordissue.arecord]) 
        (:import [defrecordissue.arecord ARecord])) 
    
    (defprotocol AProtocol 
        (afn [this])) 
    
    (extend-protocol AProtocol 
        ARecord 
        (afn [this] 42)) 
    
  3. Một không gian tên thứ ba xây dựng một thể hiện của bản ghi và gọi hàm giao thức trên bản ghi.

    (ns defrecordissue.aot1 
        (:require [defrecordissue.aprotocol] 
          [defrecordissue.arecord])) 
    
    (defrecordissue.aprotocol/afn (defrecordissue.arecord/->ARecord)) 
    

Khi namespace defrecordissue.aot1 được biên dịch, trong trường hợp của tôi sử dụng lein compile defrecordissue.aot1, biên soạn không thành công với các ngoại lệ sau đây:

Exception in thread "main" java.lang.IllegalArgumentException: No implementation of method: :afn of protocol: #'defrecordissue.aprotocol/AProtocol found for class: defrecordissue.arecord.ARecord, compiling:(aot1.clj:5:1) 
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3463) 
    at clojure.lang.Compiler.compile1(Compiler.java:7153) 
    at clojure.lang.Compiler.compile(Compiler.java:7219) 
    at clojure.lang.RT.compile(RT.java:398) 
    at clojure.lang.RT.load(RT.java:438) 
    at clojure.lang.RT.load(RT.java:411) 
    at clojure.core$load$fn__5018.invoke(core.clj:5530) 
    at clojure.core$load.doInvoke(core.clj:5529) 
    at clojure.lang.RestFn.invoke(RestFn.java:408) 
    at clojure.core$load_one.invoke(core.clj:5336) 
    at clojure.core$compile$fn__5023.invoke(core.clj:5541) 
    at clojure.core$compile.invoke(core.clj:5540) 
    at user$eval7.invoke(NO_SOURCE_FILE:1) 
    at clojure.lang.Compiler.eval(Compiler.java:6619) 
    at clojure.lang.Compiler.eval(Compiler.java:6609) 
    at clojure.lang.Compiler.eval(Compiler.java:6582) 
    at clojure.core$eval.invoke(core.clj:2852) 
    at clojure.main$eval_opt.invoke(main.clj:308) 
    at clojure.main$initialize.invoke(main.clj:327) 
    at clojure.main$null_opt.invoke(main.clj:362) 
    at clojure.main$main.doInvoke(main.clj:440) 
    at clojure.lang.RestFn.invoke(RestFn.java:421) 
    at clojure.lang.Var.invoke(Var.java:419) 
    at clojure.lang.AFn.applyToHelper(AFn.java:163) 
    at clojure.lang.Var.applyTo(Var.java:532) 
    at clojure.main.main(main.java:37) 
Caused by: java.lang.IllegalArgumentException: No implementation of method: :afn of protocol: #'defrecordissue.aprotocol/AProtocol found for class: defrecordissue.arecord.ARecord 
    at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:541) 
    at defrecordissue.aprotocol$fn__40$G__35__45.invoke(aprotocol.clj:5) 
    at clojure.lang.AFn.applyToHelper(AFn.java:161) 
    at clojure.lang.AFn.applyTo(AFn.java:151) 
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3458) 
    ... 25 more 

Nếu tôi thay đổi 3) để xây dựng các kỷ lục lớp học trực tiếp, như vậy:

(ns defrecordissue.aot2 
    (:require [defrecordissue.aprotocol] 
      [defrecordissue.arecord])) 

(defrecordissue.aprotocol/afn (defrecordissue.arecord.ARecord.)) 

Biên dịch thành công.

Nghi ngờ của tôi là điều này liên quan đến cách nào đó liên quan đến http://dev.clojure.org/jira/browse/CLJ-371, nhưng tôi không hiểu chính xác những gì đang xảy ra.

Tôi cũng nên thêm rằng không có lein clean, quá trình biên dịch thành công lần thứ hai vì lớp học cho bản ghi hiện có sẵn trên đường dẫn lớp học . Vì vậy, tôi có thể giải quyết vấn đề này bằng cách biên dịch AOT- không gian tên xác định loại bản ghi.

Tôi tạo ra một dự án leiningen đơn giản trên GitHub minh họa vấn đề , xem README để sử dụng: https://github.com/ragnard/defrecordissue

Tại sao tôi lại thấy hành vi này, và cách chính xác để tránh nó là gì?

CẬP NHẬT

Tôi đã thêm một chi nhánh mới vào repo GitHub minh họa rõ hơn về vấn đề cốt lõi: https://github.com/ragnard/defrecordissue/tree/more-realistic/

Vấn đề xảy ra bất kể ở đâu (tức là trong đó không gian tên.) Kỷ lục dụ là được xây dựng.

+0

Ý tưởng hay, ngoại trừ cùng một ngoại lệ. Tôi nghĩ 'defrecordissue.arecord' sẽ được nạp đầu tiên trong mọi trường hợp, vì nó cũng được yêu cầu trong' defrecordissue.aprotocol' – Ragge

+0

Nhận xét trên có liên quan đến một nhận xét khác dường như đã bị xoá. – Ragge

Trả lời

2

Tôi có thể tạo lại vấn đề với repo của bạn.Dưới đây là ba giải pháp mà làm việc cho tôi:

  1. Nói lein compile để biên dịch nhiều không gian tên:

    lein compile defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1 
    
  2. Đặt

    :aot [defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1] 
    

    trong project.clj.

  3. Đặt

    :aot :all 
    

    trong project.clj.

Sau hai làm lein compile làm công việc của lein aot1 (trong trường hợp 2.) và cả hai lein aot1lein aot2 (trong trường hợp 3).

+0

Cảm ơn câu trả lời của bạn. Tôi biết tôi có thể giải quyết vấn đề bằng cách AOT biên dịch không gian tên 'defrecordissue.arecord'. Tuy nhiên, đây không phải là hoàn toàn thỏa đáng trong trường hợp xảy ra sự cố. Trong trường hợp đó là , cả hai 'defrecordissue.arecord' và ' defrecordissue.aprotocol' là một phần của thư viện và 'defrecordissue.aot1' chỉ là một khách hàng của thư viện đó và không biết về loại bản ghi (hoặc ngay cả giao thức). Nó không hiển thị trực tiếp các loại bản ghi, như trong ví dụ này. Tôi muốn cung cấp thư viện mà không có bất kỳ lớp học được biên dịch AOT nào nếu có thể . – Ragge

0

Tôi thường xuyên chạy vào thời gian này. Đây là những gì tôi đã đưa ra và nó dường như làm việc:

(defmacro with-datatype 
    [datatype & body] 
    (let [last-dot (.lastIndexOf ^String (str datatype) ".") 
     ns (-> datatype 
       str 
       (subs 0 last-dot) 
       symbol)] 
    `(do 
     (require (quote ~ns)) 
     (import ~datatype) 
     [email protected]))) 

(defmacro extend-type* 
    [datatype & extensions] 
    `(with-datatype ~datatype 
    (extend-type ~datatype 
     [email protected]))) 

(defmacro extend-protocol* 
    [protocol & specs] 
    (let [splitter (let [l (atom nil)] 
        #(if (symbol? %) (reset! l %) @l)) 
     specs (partition-by splitter specs)] 
    `(do 
     [email protected](for [[datatype & extensions] specs] 
      `(extend-type* ~datatype ~protocol [email protected]))))) 

Sau đó, bạn chỉ cần thay đổi khối mã thứ hai của bạn để:

(ns defrecordissue.aprotocol) 

(defprotocol AProtocol 
    (afn [this])) 

(extend-protocol* AProtocol 
    defrecordissue.arecord.ARecord 
    (afn [this] 42)) 

Có lẽ không phải là giải pháp sạch, nhưng bạn don không phải AOT thư viện của bạn nữa.

Các vấn đề liên quan