7
votes

I'm attempting to use deftype (from the bleeding-edge clojure 1.2 branch) to create a java class that implements the java Servlet interface. I would expect the code below to compile (even though it's not very useful).

(ns foo [:import [javax.servlet Servlet ServletRequest ServletResponse]])

(deftype servlet [] 
   javax.servlet.Servlet 
   (service [this 
         #^javax.servlet.ServletRequest request
         #^javax.servlet.ServletResponse response]
   nil))

But it doesn't compile. The compiler produces the message:

Mismatched return type: service, expected: void, had: java.lang.Object
  [Thrown class java.lang.IllegalArgumentException]

Which doesn't make sense to me, because I'm returning nil. So the fact that the return type of the method is void shouldn't be a problem. For instance, for the java.util.Set interface:

(deftype bar [#^Number n] java.util.Set (clear [this] nil))

compiles without issue.

So what am I doing wrong with the Servlet interface?

To be clear: I know that the typical case is to subclass one of the servlet abstract classes rather than implement this interface directly, but it should still be possible to do this.

Stack Trace:

The stack trace for the (deftype servlet... is:

Mismatched return type: service, expected: void, had: java.lang.Object
  [Thrown class java.lang.IllegalArgumentException]

Restarts:
 0: [ABORT] Return to SLIME's top level.

Backtrace:
  0: clojure.lang.Compiler$NewInstanceMethod.parse(Compiler.java:6461)
  1: clojure.lang.Compiler$NewInstanceExpr.build(Compiler.java:6119)
  2: clojure.lang.Compiler$NewInstanceExpr$DeftypeParser.parse(Compiler.java:6003)
  3: clojure.lang.Compiler.analyzeSeq(Compiler.java:5289)
  4: clojure.lang.Compiler.analyze(Compiler.java:5110)
  5: clojure.lang.Compiler.analyze(Compiler.java:5071)
  6: clojure.lang.Compiler.eval(Compiler.java:5347)
  7: clojure.lang.Compiler.eval(Compiler.java:5334)
  8: clojure.lang.Compiler.eval(Compiler.java:5311)
  9: clojure.core$eval__4350.invoke(core.clj:2364)
 10: swank.commands.basic$eval_region__673.invoke(basic.clj:40)
 11: swank.commands.basic$eval_region__673.invoke(basic.clj:31)
 12: swank.commands.basic$eval__686$listener_eval__687.invoke(basic.clj:54)
 13: clojure.lang.Var.invoke(Var.java:365)
 14: foo$eval__2285.invoke(NO_SOURCE_FILE)
 15: clojure.lang.Compiler.eval(Compiler.java:5343)
 16: clojure.lang.Compiler.eval(Compiler.java:5311)
 17: clojure.core$eval__4350.invoke(core.clj:2364)
 18: swank.core$eval_in_emacs_package__320.invoke(core.clj:59)
 19: swank.core$eval_for_emacs__383.invoke(core.clj:128)
 20: clojure.lang.Var.invoke(Var.java:373)
 21: clojure.lang.AFn.applyToHelper(AFn.java:169)
 22: clojure.lang.Var.applyTo(Var.java:482)
 23: clojure.core$apply__3776.invoke(core.clj:535)
 24: swank.core$eval_from_control__322.invoke(core.clj:66)
 25: swank.core$eval_loop__324.invoke(core.clj:71)
 26: swank.core$spawn_repl_thread__434$fn__464$fn__465.invoke(core.clj:183)
 27: clojure.lang.AFn.applyToHelper(AFn.java:159)
 28: clojure.lang.AFn.applyTo(AFn.java:151)
 29: clojure.core$apply__3776.invoke(core.clj:535)
 30: swank.core$spawn_repl_thread__434$fn__464.doInvoke(core.clj:180)
 31: clojure.lang.RestFn.invoke(RestFn.java:398)
 32: clojure.lang.AFn.run(AFn.java:24)
 33: java.lang.Thread.run(Thread.java:637)
1

1 Answers

20
votes

Try without any type hints:

(deftype servlet []
  javax.servlet.Servlet
  (service [this request response]
    ...body...))

From the web page about deftype:

  • if you leave out all hints: will try to match on same name/arity method in interface(s)

    • this is preferred
    • if you supply any hints at all, no inference is done, so all hints (or default of Object) must be correct, for both arguments and return type

And from (doc deftype):

If not supplied, they will be inferred, so type hints should be reserved for disambiguation.