Fork me on GitHub
#beginners
<
2020-05-30
>
sylvain09:05:35

I'm writing some code where many of the functions in my namespace take the same first 2 parameter types as input so I end up with code that looks like this:

(defn some-function1 [state block]
  (let [local-func1 (partial func1 state block)
        local-func2 (partial func2 state block)
        local-func3 (partial func3 state block)

        param1 (local-func1 "data0")
        param2 (local-func3 {:some-key "data1"})
        param3 (local-func1 "data3")
        param4 (local-func2 "data4")]
    (-> state
        (func4 block param1 param2)
        (func5 block param3)
        (func5 block param4)
        (func6 "data5"))))

(defn some-function2 [state block value]
  (let [local-func2 (partial func2 state block)
        local-func4 (partial func4 state block)
        
        param1 (local-func2 "data6")]
    (-> state
        (func4 block value)
        (func5 block param1))))

(defn some-function3 [state block other-value]
  ;; ...
  )
I feel like this could be improved (e.g not having to manually bind the local functions at the beginning of the let would be nice) but I'm not sure how to go about it. Any suggestions ? Is this a case where using one of those monadic libraries would help ?

aisamu16:05:58

If you don't want bindings, (and since you only use the functions once) you could make a helper function that makes those closures for you

(defn some-function1 [state block]
  (let [with-context #(apply % state block %&)
        param1 (with-context func1 "data0")
        param2 (with-context func3 {:some-key "data1"})
        ...

rymndhng05:05:12

without context about what block is — it’d be pretty hard to say. Also, at some point in your application, there’s going to be a complex piece that takes all your little functions to do something useful. This seems like that function, and from experience, trying to reduce the lines of code or hide the binding could introduce difficulty debugging. Something else to keep in mind is that the fewer intermediate vars you introduce, the fewer good names you have to find. I think the code without the partial functions is fairly reasonable:

(defn some-function1 [state block]
  (let [param1 (func1 state block "data0")
        param2 (func3 state block {:some-key "data1"})
        param3 (func1 state block "data3")
        param4 (func2 state block "data4")]
    (-> state
        (func4 block param1 param2)
        (func5 block param3)
        (func5 block param4)
        (func6 "data5"))))

sylvain07:05:52

Thanks for your answers. I need to mull over this.

michaelb11:05:07

this is more of a general jvm thing, I guess, but I’m developing with Clojure so maybe someone here will have a suggestion… Using jpackage in OpenJDK 14 I’m packaging my app’s uberjar (built with lein) for Linux, macOS, and Windows. On Windows only, the installed app (i.e. installed with the installer created by jpackage) sometimes unexpectedly closes a few seconds after opening it. I have not seen that behavior on Windows when e.g. running the code from a REPL, so I’m really not sure if it’s something wrong with my code, or if it’s something buggy re: the artifacts created by jpackage. When the app unexpectedly closes, there’s no error dialog, so no clue yet what’s going on. On the assumption it’s something that can be caught and logged (e.g. written to C:\appcrash.log) how can I go about doing that in the most general way possible since it’s not clear where to put try/catch?

Lukas11:05:38

Hey guys, I wrote a simple web server jetty/ring. I have my server running in the repl (emacs cider) and I would like to reload my changes without closing my repl and starting it again. How can I do this?

v11:05:12

There is a middleware called ring.middleware.reload. That should do the job

seancorfield16:05:33

Beware of reload/refresh workflows: they seem "easy" but they are not "simple" and you can get into difficulties with them.

seancorfield16:05:00

A much better workflow is to simply eval each change, as you make it, into the running image.

seancorfield16:05:43

It requires discipline tho'. You must get into the habit of eval'ing every single function change as you make it. I "evaluate top block" each time I change a function or add a new function. I also take care to use #'my-fn instead of plain my-fn where functions are passed as arguments (such as the handler function passed into Ring middleware or into the server start function). That extra indirection allows the process to see changes to functions after compilation.

👍 3
Lukas16:05:50

Thanks a lot, :thinking_face: I guess I lack some fundamentals here 🙈. I started my repl, required the namespace of my server and did start the server. Everytime i just evalualted my changes it didnt refresh the output. I have a simple handler that just prints some basic html and i tried to change the string -> evaluate and the change did not apply after reloading the page

Lukas16:05:09

@U04V70XH6 thanks again! Especially for the tip using this #'my-fn

seancorfield16:05:07

See https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs (the whole REPL guide is pretty good but that section in particular is important).

❤️ 1
Lukas16:05:40

Hey, I'm using ring and the cookie middleware. I attach this map to the cookie :cookies {:session-id {:value "hash?"}} but when I get it back in the next request :session-id changed to a string e.g. {"session-id" {:value "hash?"}} which feels kinda unnatural to work with. Is there a good workaround for this issue (change it back to clojure key)?

lsenjov16:05:52

Write your own middleware, throw it further down?

lsenjov16:05:13

Middleware is pretty simple to write

(defn wrap-my-middleware
  [handler]
  (fn [request]
    (-> request do-before handler do-after)))

👍 1
Lukas16:05:28

thanks you

lsenjov16:05:37

Although it may be an idea just to work with it as a string

lsenjov16:05:51

Because other middleware libraries will also want to work with it as a string

✔️ 1
1