Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unquote doesn't work in profiles.clj :repl-options #1199

Closed
benwbooth opened this issue Jun 2, 2013 · 16 comments
Closed

unquote doesn't work in profiles.clj :repl-options #1199

benwbooth opened this issue Jun 2, 2013 · 16 comments
Labels

Comments

@benwbooth
Copy link
Contributor

Here is my ~/.lein/profiles.clj:

{:user
 {:plugins [[lein-pprint "1.1.1"]]
  :dependencies [[incanter "1.5.0-SNAPSHOT"]]
  :repl-options
  {:value ~(fn [x] (print "test") (print x))}
  }}

Here is what I get in lein repl after I type any expression that returns a value:

ben@mini:~$ lein repl

nREPL server started on port 62264
REPL-y 0.2.0
Clojure 1.5.1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)

user=> 1
ClassCastException clojure.lang.Cons cannot be cast to clojure.lang.IFn
  reply.eval-modes.nrepl/execute-with-client (nrepl.clj:93)
  clojure.core/apply (core.clj:621)
  clojure.core/partial/fn--4192 (core.clj:2398)
  clojure.core/map/fn--4207 (core.clj:2487)
  clojure.lang.LazySeq.sval (LazySeq.java:42)
  clojure.lang.LazySeq.seq (LazySeq.java:60)
  clojure.lang.RT.seq (RT.java:484)
  clojure.core/seq (core.clj:133)
  clojure.core/dorun (core.clj:2780)
  clojure.core/doall (core.clj:2796)
  reply.eval-modes.nrepl/run-repl (nrepl.clj:134)
  reply.eval-modes.nrepl/main (nrepl.clj:245)

It looks like unquoting is not working for some reason, but I have no idea why. I would like the (fn) form to be evaluated as a function instead of a Cons. The sample.project.clj file seems to suggest that this is possible. Why doesn't this work?

@trptcolin
Copy link
Collaborator

The magic leiningen unquoting is only a feature of the defproject macro and doesn't currently work with profiles. This actually does already work when used in a defproject, though it's an undocumented feature of REPLy that you can't necessarily rely on working the same in future releases :)

To the larger question: adding the ability to unquote in profiles is technically possible, but would require an eval where we don't currently have one, after reading profiles. I spiked it out on the profile_unquoting branch, but I suspect there are edge cases, as usual with profiles, so I don't feel super-confident (the eval also feels a bit unseemly).

@technomancy
Copy link
Owner

Even if you could unquote in profiles, this wouldn't work because it would involve compiling a function and then sending it to a subprocess, and there is no way to decompile a function for serialization at present.

@trptcolin
Copy link
Collaborator

True, but for this use case it does work, unless I'm missing some subtlety. That [undocumented] reply feature is used in reply directly, in the lein process, not sent through the nREPL.

@benwbooth
Copy link
Contributor Author

That's right, the :value option is used on the client, and is not sent from server to client.

On Jun 1, 2013, at 9:01 PM, Colin Jones [email protected] wrote:

True, but for this use case it does work, unless I'm missing some subtlety. That [undocumented] reply feature is used in reply directly, in the lein process, not sent through the nREPL.


Reply to this email directly or view it on GitHub.

@technomancy
Copy link
Owner

Oh, I see. In that case I would say it's the repl's task to turn an (fn [x y z] ...) form from a list into a compiled function. You might be able to work around it with clojure's read-eval, but we shouldn't require folks to fall back on that as it's a bit weird. What does :value do?

@technomancy technomancy reopened this Jun 2, 2013
@benwbooth
Copy link
Contributor Author

On Jun 1, 2013, at 9:38 PM, Phil Hagelberg [email protected] wrote:

What does :value do?

It lets you tweak how the value sent from the server gets displayed on the clien repl. I'd like to use it to make a paged output for large incanter datasets by piping the output to less. That way I can scroll around and view the dataset with arrow keys.


Reply to this email directly or view it on GitHub.

@trptcolin
Copy link
Collaborator

@technomancy Yes, when reply actually supports this feature, it'll be treated just like the :prompt/:subsequent-prompt and other options, where you can say :repl-options {:value (fn [x] (print "test: ") (print x))}, no unquote.

@trptcolin
Copy link
Collaborator

@benbooth5 Fair warning: you won't be able to completely treat these results as a real stream, because values come back from nREPL as single messages. So you'll have to realize the whole result set at once, and the changes you're talking about would just be a view into it.

Beyond that, I'm not sure how you plan to pipe output to less (using a quick clojure.java.shell call to sh -c 'echo "..." | less' doesn't give less the feedback it needs to pause on each page), but I'll let you shave that yak :) I do like the idea, though!

@benwbooth
Copy link
Contributor Author

@trptcolin This is what I have so far, although I haven't tested it yet:

(defn page [x]
  (binding [*out* (java.io.OutputStreamWriter.
                    (.getOutputStream (.exec (Runtime/getRuntime)
                                            (into-array String [(or (System/getenv "PAGER") "less")]))))]
    (print x)))

@benwbooth
Copy link
Contributor Author

@trptcolin Hmm.. I tried it with the read-eval trick. Looks like the input/output of the Runtime.exec process isn't connected to the terminal. This is going to require more experimentation...

@benwbooth
Copy link
Contributor Author

@trptcolin Got it working! Here's the working code in case anyone's interested. Java 7 required:

{:user
 {:plugins [[lein-pprint "1.1.1"]]
  :dependencies [[incanter "1.5.0-SNAPSHOT"]]
  :repl-options 
  {:value
   #=(eval
      (fn [x]
        (let [p (-> (ProcessBuilder. (into-array String [(or (System/getenv "PAGER") "less")]))
                    (.redirectOutput java.lang.ProcessBuilder$Redirect/INHERIT)
                    (.start))]
          (binding [*out* (java.io.OutputStreamWriter. (.getOutputStream p))]
            (println x)
            (.close *out*)
            (.waitFor p)))))
   }}}

@technomancy
Copy link
Owner

Should we make changes in Leiningen to support this? Maybe eval :value before passing it to reply?

@trptcolin
Copy link
Collaborator

I don't think it's Lein's job to know about eval-ing this. I still think letting unquoting work in profiles, as in https://github.com/technomancy/leiningen/tree/profile_unquoting, would be nice, though. Is there more complexity I'm missing around unquoting in profiles beyond the existing unquoting?

@technomancy
Copy link
Owner

I would actually rather get rid of our unquoting implementation in project.clj in favour of read-eval now that it's ... less undocumented.

It seems like if reply takes an argument that it needs to be a function then Leiningen should be able to handle convert the setting from project.clj. It could do so in a way that would preserve values that were already ifn? though.

@trptcolin
Copy link
Collaborator

Oh yeah, that's fine w/ me, I just want consistency. That's probably less surprising since there are no explicit syntax-quotes typically.

REPLy will not require a real fn once trptcolin/reply#118 is done - I'm already handling some other options with conversions.

@technomancy
Copy link
Owner

OK, I'll close this here if it's being tracked in reply; thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants