Fork me on GitHub
#clojure
<
2022-12-11
>
hanDerPeder12:12:59

any way to pass local bindings to eval? trying to get this to work:

(let [x 10]
  (eval 'x))

p-himik12:12:16

You can wrap the eval'ed form in an extra let:

user=> (let [x 10, form 'x] (eval `(let [~'x ~x] ~form)))
10
But unless you're absolutely adamant that you need eval, there might be a better solution to the overall problem.

hanDerPeder21:12:01

Thanks. Your suspicion is correct. It's "why doesn't this work?", not "I need this to work".

igrishaev07:12:20

It has been described here but in Russian: https://grishaev.me/clj-repl-part-3/ You may use google translate

igrishaev07:12:44

TL:DR: first you need this macro:

(defmacro get-locals []
  (into {} (for [sym (keys &env)]
             [(list 'quote sym) sym])))

igrishaev07:12:40

then a custom version of eval called eval+

(defn eval+ [ns locals form]
  (binding [*locals* locals
            *ns* ns]
    (eval `(let ~(reduce
                  (fn [result sym]
                    (conj result sym `(get *locals* '~sym)))
                  []
                  (keys locals))
             ~form))))

igrishaev07:12:49

(def ^:dynamic ^:private
  *locals* nil)

igrishaev07:12:12

(eval+ *ns* {'a 1 'b 2} '(+ a b))
3

hanDerPeder08:12:01

Clever, thanks!

sheluchin14:12:25

I'm looking to improve error handling in my project. Looks like there are quite a few popular options: just using ex-info, exoscale/ex, slingshot, cognitect-labs/anomalies, fmnoise/flow, and more. It's a little bit hard to understand the trade-offs between these. In general, formalising the use of ex-info sounds like a good thing, as long as it doesn't add too much complexity. Does anyone have any input that would help me choose?

valtteri14:12:00

I’d say it depends. Maybe it helps if you describe what kind of error handing problems you’re currently having in your project? And then maybe think which aspects would you like to improve and then continue to the “how” part.

👍 1
pavlosmelissinos14:12:41

exoscale/ex, slingshot and fmnoise/flow all provide their own constructs to deal with exceptions, so you have to replace your error handling code in order to use them. It's an interesting direction that might be worth exploring if you're starting a new project but I don't really see the value over plain data. In my experience, if you have a "contract" for error types (what cognitect-labs/anomalies offers), ex-info is enough.

mpenet15:12:17

exoscale/ex is basically anomalies + try macro and a custom hierarchy

👍 1
mpenet15:12:11

You can easily pick & choose. We made that to have conventions over all our services

sheluchin15:12:45

It's not so much that I'm having problems with my current code, it's moreso that I've been naively coding down the "happy path" for the most part, and haven't really had a cohesive approach or plan for dealing with errors. It has worked out okay for now, but I recognize the room for improvement around error handling and logging. I guess you could say I have a blank canvas.

pavlosmelissinos15:12:06

exoscale/ex is actually the first third-party library I'd try if I saw signs that my approach is not enough. I like that ex uses keywords as enums to distinguish between errors, as well as that it supports exception hierarchies and I can see the value in a bigger organization. For what it's worth, the pattern I currently use is a single ExceptionInfo catch and a case based on the values of :cognitect.anomalies/category in the ex-data of the exception that I want to handle. So the first example on the exoscale/ex README would become:

(require '[cognitect.anomalies :as ex])
(import '[clojure.lang ExceptionInfo])

(try

   (throw (ex-info "Argh" {::ex/category ::bar :foo "a foo"}))

   (catch ExceptionInfo e
     (let [data (ex-data e)]
       (case (::ex/category data)
         ::foo (prn :got-ex-data data)
         ::bar (prn :got-ex-data-again (:foo data))
         nil)))

   (catch Exception e (prn :boring))

   (finally (prn :boring-too)))

Joshua Suskalo16:12:09

Just gonna throw my hat in the ring here: there's no reason to limit yourself to just one error handling strategy. It is useful to determine which error handling strategies are most useful in what context. Personally my needs for error handling have best been met by using fmnoise/flow and returning error values for domain errors, that is errors that are not "exceptional circumstances" in the domain but rather a normal part of it. To go along with domain error handling, I usually use a different library for IO errors that are a part of dealing with a fallible world rather than due to inherent complexity of the domain. These are often better to use complex control flow operations with, and slingshot or others may be extensible to interoperate with flow because of flow's extension mechanism. For my own choice I use #farolero which I wrote and that implements Common Lisp style conditions/restarts (the other libraries that implement conditions/restarts aren't really CL-style), and it has a built-in mechanism for working with flow.

👀 1
sheluchin17:12:58

Thanks for explaining your approach and showing how it would work with ex, @UEQPKG7HQ. That looks pretty succinct and easy to understand.

sheluchin17:12:03

@U5NCUG8NR Thank you for your input. I understand your point about mixing strategies where necessary. I think that approach can certainly have its advantages, but it seems like a more advanced model that I would rather arrive at gradually after starting with something small and simple to understand. I actually found fmnoise/flow by reading one of your older comments in a thread here somewhere. You mentioned that you like to use it in data pipelines. That is an interesting angle to me because much of my project is doing ETL. The thing is, the fmnoise/flow interface seems largely designed for use in the thread-last macro, and I'm mostly using transducers. I'm sure there's a way I could make it work still, but I'm thinking maybe it's just not a natural fit. Farolero looks interesting. I like the idea of "separating reporting, reconciliation, and recovery". I just don't have any familiarity at all with CL, so there are many new concepts here that will take me some time to read over.

Joshua Suskalo17:12:26

Yeah, that's absolutely fair. I try to make sure that the readme doesn't require any prior knowledge and you should be able to work through it just from the readme and the examples I have in the doc directory. I would agree that flow could be more ergonomic to use in transducers though.