Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure giving reflection warnings depsite type hints

I'm writing a chat client/server, and have the following code that takes a socket, and gives back a clojure.core.async/channel that can be used to write to the socket more easily.

My problem is, despite being type-hinted, I still get reflection warnings that the call to .write can't be resolved.

(ns chat.main
  (:require [clojure.core.async :as a :refer [>! >!! <! <!! go thread chan]])

  (:import [java.net Socket ServerSocket]
           [java.io InputStream BufferedReader InputStreamReader OutputStream BufferedOutputStream PrintWriter]))

(set! *warn-on-reflection* true)

(defn new-output-chan [^Socket sock]
  (let [^OutputStream out (.getOutputStream sock)
        ^PrintWriter p-out (PrintWriter. out)
        out-chan (chan)]
    (go
      (while (.isConnected sock)
        (let [^String message (<! out-chan)]
          (.write p-out message) ; <---------- Here
          (.flush p-out)))
      (a/close! out-chan))
    out-chan))

Gives:

Reflection warning, C:\Users\slomi\IdeaProjects\chat\src\chat\main.clj:44:5 - call to method write on java.io.PrintWriter can't be resolved (argument types: unknown).

I don't know how I could make it any clearer though. Both p-out and message are explicitly hinted. Strangely, the call to flush is fine.

How can I resolve this?

like image 663
Carcigenicate Avatar asked Mar 11 '23 11:03

Carcigenicate


1 Answers

The problem is that go actually rewrites your code into a state machine (you can inspect the transformation with macroexpand-1, but the result is pretty lengthy, so I haven't included it here). Unfortunately, it looks like this transformation doesn't preserve the type hint.

Maybe there's a better solution, but if you pull out the call into its own function, you can typehint that, since it won't be rewritten by go:

(defn new-output-chan [^Socket sock]
  (let [^OutputStream out (.getOutputStream sock)
        ^PrintWriter p-out (PrintWriter. out)
        out-chan (chan)
        write #(.write p-out ^String %)]
    (go
      (while (.isConnected sock)
        (let [message (<! out-chan)]
          (write message)
          (.flush p-out)))
      (a/close! out-chan))
    out-chan))
like image 110
Beyamor Avatar answered Apr 02 '23 22:04

Beyamor