redplanetlabs / proxy-plus

A replacement for Clojure's proxy that's 10x faster and more usable
Apache License 2.0
175 stars 10 forks source link

How can I use this with custom methods or clojure protocols? #15

Closed ieugen closed 2 years ago

ieugen commented 2 years ago

Hello,

I am trying to extend an abstract class and also implement custom methods (not belonging to an interface / abstract class). Is this possible?

I tried but it failed with error.

I also tried defining a protocol:


(defprotocol ICsvTable
  "Some extra CSV table methods"
  (isStream [this] "Return true if table is stream")
  (getFieldTypes [this type-factory] "Returns the field types of this CSV table."))

(defn csv-table
  [^Source source ^RelProtoDataType proto-row-type]
  (let [field-types (ArrayList.)])
  (proxy+ []
          AbstractTable
          (getRowType
           [this type-factory]
           (get-row-type this source type-factory proto-row-type row-type proto-row-type))
          ICsvTable
          (isStream [this] false)
          (getFieldTypes
           [this type-factory]
           (when (.isEmpty field-types)
             (CsvEnumerator/deduceRowType type-factory source field-types (.isStream this))))))

and I got this (cryptic) error message:

; Evaluating file: calcite_csv.clj
; Syntax error macroexpanding proxy+ at (src/ro/ieugen/calcite_csv.clj:76:3).
; No implementation of method: :asm-type of protocol: #'com.rpl.asm/ASMType found for class: clojure.lang.Var
; Evaluation of file calcite_csv.clj failed: class clojure.lang.Compiler$CompilerException
nathanmarz commented 2 years ago

proxy+ doesn't understand protocols. You can instead reference the underlying interface, which would be <namespace>.ICsvTable

ieugen commented 2 years ago

Sorry to bother but it does not work for me.

(ns ro.ieugen.inerface)

(defprotocol ICsvTable
  "Some extra CSV table methods"
  (isStream [this] "Return true if table is stream")
  (getFieldTypes [this type-factory] "Returns the field types of this CSV table."))
(defn csv-table
  [^Source source ^RelProtoDataType proto-row-type]
  (let [row-type (atom nil)]
    (proxy+ []
            AbstractTable
            (getRowType [this type-factory]
                        (get-row-type this source type-factory proto-row-type row-type proto-row-type))

            ro.ieugen.interface.ICsvTable
            (isStream [this] false)
            (getFieldTypes
             [this type-factory]
             (println "hello")))))
clj꞉ro.ieugen.calcite-csv꞉> 
; Evaluating file: calcite_csv.clj
; Syntax error macroexpanding proxy+ at (src/ro/ieugen/calcite_csv.clj:70:5).
; Could not resolve symbol
; Evaluation of file calcite_csv.clj failed: class clojure.lang.Compiler$CompilerException

If I take out the lines with and after ro.ieugen.interface.ICsvTable it works ok.

Can the error message be improved ? It's not very helpful as is.

nathanmarz commented 2 years ago

Perhaps when the proxy+ macro is expanded the protocol hasn't been defined yet. Try putting the protocol definition in a separate file.

ieugen commented 2 years ago

Putting it in another file worked. I tried that but introduced a typo and I got confused in the process. Thank you again for confirming this.

I'm working on translating the Apache Calcite CSV example to Clojure. Calcite uses quite a few abstract classes. It was getting hard to re-implement all of that Java code with reify and proxy.

Thanks for writing this.