beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
nate 2020-10-11T01:07:22.133700Z

Parens of the dead was a really fun one to watch: http://www.parens-of-the-dead.com/

Eric Ihli 2020-10-11T02:08:05.138600Z

I'm trying to write a macro that wraps a bunch of forms in try/catch blocks.

(defmacro try-forms [bindings & children]
  `(let* ~(destructure bindings)
     (map
      (fn [child#]
        (try
          child#
          (catch Exception e# (println
                               (format "Couldn't evaluate %s because of %s" child# e#)))))
      '[~@children])))

(try-forms
 [some-float 5.0]
 (format "val: %f" some-float)
 (format "val: %d" some-float))
;; => ((format "val: %f" some-float) (format "val: %d" some-float))
That's close. I'm getting a list of the forms that I want. They just aren't being evaluated. I could think to try wrapping child# in an eval, but that would try to evaluate the literal form child# and not the form that child# represents ( (format "val: %f" some-float)), right? I'm having a hard time discovering the solution to this.

walterl 2020-10-11T11:26:46.139300Z

Looks like you probably want this:

walterl 2020-10-11T11:27:08.139700Z

Which expands like so:

; eval (root-form): (macroexpand '(try-forms [some-float 5.0] (format "val: %f" some-float...
(let*
 [some-float 5.0]
 (try
  (format "val: %f" some-float)
  (catch
   java.lang.Exception
   e__69339__auto__
   (clojure.core/println
    (clojure.core/format
     "Couldn't evaluate %s because of %s"
     "(format \"val: %f\" some-float)"
     e__69339__auto__))))
 (try
  (format "val: %d" some-float)
  (catch
   java.lang.Exception
   e__69339__auto__
   (clojure.core/println
    (clojure.core/format
     "Couldn't evaluate %s because of %s"
     "(format \"val: %d\" some-float)"
     e__69339__auto__)))))

Eric Ihli 2020-10-11T13:58:19.147500Z

Thanks walterl! That makes sense. I'm, going to go play around with it some more.

Jim Newton 2020-10-11T11:36:34.143900Z

There is an interesting feature/bug/loophole in the Record concept w.r.t. namespaces. In one namespace I've defined a Record and a slew of functions to manipulate the instances thereof. actually I've defined several different Records in several different namespaces which are independent from each other. the objects form different types of graphs to represent particular problems. In a 3rd namespace, I have functions which output the graphs to graphviz dot format. The xyz-to-dot functions can access the record instances as if they were hash maps. This means there is no need t make my dot namespace require the namespaces of the records for which the code is manipulating.

Jim Newton 2020-10-11T11:37:38.144500Z

this means the namespace topology of my project does not match the actual loading requirements of my program.

Jim Newton 2020-10-11T11:37:59.144800Z

not sure whether this is a feature or a bug.

2020-10-11T12:00:09.145Z

I am not sure which aspect of this you consider to be a loophole or bug, but if it is that you expected that the fields of records to be private/hidden/inaccessible-without-access-privileges, then I think that is considered a feature of Clojure, not a bug. 'Immutable data doesn't need encapsulation' is a rough paraphrase of something Rich Hickey has said on multiple occasions -- if I can find an actual quote I will link it here.

2020-10-11T12:03:51.145200Z

Search for the word "hiding" in this interview, as one example: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/RichHickeyQandA.md

2020-10-11T12:07:31.145800Z

In Clojure, records are intended to look like maps. defrecord is a thing in case you want to use the new type for defprotocol, and for lookup and memory efficiency of the record fields you define. But you can assoc new keys onto a record, and get back a normal Clojure map if you add a key that isn't defined in the defrecord type.

2020-10-11T12:08:00.146Z

Some of this is from the http://clojure.org doc page on data types, which discusses properties of defrecord and deftype: https://clojure.org/reference/datatypes

2020-10-11T12:10:32.146200Z

I believe that if you want to actually create objects in memory that are instances of that record type, you cannot (easily) do so without require'ing the namespace with the defrecord form.

2020-10-11T12:11:04.146400Z

Reading their fields as if they were a regular Clojure map can be done without that.

Panagiotis Mamatsis 2020-10-11T14:00:25.150600Z

Good afternoon everyone. I was seeing a video on YouTube. It's called "Clojure for Java Programmers Part 1". At some point Ritch mentions that you can fix a bug in a running instance of a program! I was wondering how is it possible to do that? Isn't Clojure a compiled language?

2020-10-11T14:50:16.152800Z

A typical way is to connect to the running JVM process via a REPL connection, and evaluate def and defn forms to redefine functions or values on the fly

2020-10-11T14:50:44.153700Z

Clojure’s compilation unit is not a whole file, but each top level form separately

2020-10-11T14:51:24.154900Z

Even in a REPL, each form is compiled before it is executed (compiles to JVM byte code)

Panagiotis Mamatsis 2020-10-11T15:05:11.159200Z

Oh! This means that even if my application is a jar file I can still connect to it and fix bugs?

Eric Ihli 2020-10-11T15:05:13.159300Z

Is there a way to achieve the following functionality? I want to have a flag that I can enable that will change the behavior of format so that it prints the raw forms rather than the evaluated values. Something like the code below (which doesn't work binding can't take the value of a macro.

;; Replace all %d, %,.2f, etc.. with %s. Then apply format to the new string and the unevaluated args.
(defmacro format* [s & args]
  (apply format (string/replace s #"%[^% ]+" "%s") args))

(def ^:dynamic format clojure.core/format)

(binding [format format*]
  (format "Hi %s. Half your age of %d is %.2f"
          (:name person)
          (:age person)
          (/ (:age person) 2)))

2020-10-11T15:22:20.159400Z

Yes. In practice, many people who deploy JAR files to a collection of machines often prefer to fix bugs by using more standard "update the JAR, deploy the JAR as if it was a new version" methods, but if you have a single server, or want to investigate the running data in a live server to track down some bug, the connect-via-REPL-and-evaluate-forms approach can be useful for read-only detailed investigation, too, whether you use that method to do live bug fixing or not.

2020-10-11T15:23:29.159600Z

Restricting oneself to "standard deployment mechanism" can be useful for reproducibility, e.g. the fix is checked into revision control, etc.

2020-10-11T15:25:49.159800Z

To clarify what you are trying to achieve, you want a modified version of format that in some cases works exactly like format does, but by changing some global flag then you would like the output of your example format call to be the following? Hi (:name person). Half your age of (:age person) is (/ (:age person) 2) ?

2020-10-11T15:31:57.160Z

If so, that sounds achievable by defining a new macro, e.g. named my-format, where each invocation of my-format expanded into some code like this:

(if *print-raw-forms*
  (format "Hi person with age %s." '(:age person))
  (format "Hi person with age %d." (:age person)))

2020-10-11T15:32:57.160200Z

There is no way I can think of to get the output shown above by changing the definition of a function like format, because in Clojure the args to every function call are evaluated before the call occurs.

Panagiotis Mamatsis 2020-10-11T15:57:48.160400Z

I totally agree with the last reply. But the "connect using REPL" blew my mind! I thought that BEAM based languages had this feature!!!

2020-10-11T15:59:32.160600Z

I believe that Erlang has this feature, but I haven't used it or other languages running on the BEAM VM, so don't trust my statement on the matter.

2020-10-11T16:01:00.160900Z

Not all, but many Lisp family languages have this capability, and Smalltalk family languages.

2020-10-11T16:01:45.161100Z

And Clojure isn't the only Lisp that is able to compile each form before executing it, e.g. several Common Lisp implementations can do that.

Eric Ihli 2020-10-11T16:21:55.161300Z

Thanks. So, I'm understanding that there is no reasonably convenient way to get it to "just work with code written with the existing format ".

2020-10-11T16:52:25.161500Z

If you want unevaluated arguments, then there is no reasonably convenient way to do that for any existing function, which includes format

2020-10-11T16:53:20.161700Z

It is possible for existing macros, or for new macros that you write yourself

athomasoriginal 2020-10-11T17:05:25.163Z

Macro Question: I wrote a macro that slurps in an .svg file and outputs hiccup. This is CLJS

;; macro is pseudo code
(defmacro inline-svg 
  [path]
  (assert (string? path))
  (let [hiccup (-> (slurp path)
                   (transform-to-hiccup))]
    `~hiccup))

;; callsite
(inline-svg "path/to/file.svg")
The above works great. However, if I were to change the callsite to something like
(inline-svg (:icon-name icons))
The above doesn’t work because. TMK the (:icon-name icons) isn’t evaluated. How could I evaluate the first arg first? Thanks all!

2020-10-11T17:13:51.167500Z

It is evaluated, but as a macro invocation, as you have written it, it is evaluated at compile time, i.e. when your code is loaded or require'd.

2020-10-11T17:14:53.169100Z

Is there any reason you need inline-svg to be a macro? It seems like it could just as well read "path/to/file.svg" at run time and convert it to hiccup then, couldn't it?

2020-10-11T17:16:15.170100Z

i.e. why not change inline-svg to a function?

Day Bobby 2020-10-11T17:17:09.170200Z

I have some code that determines if an action is to be executed in current environment. I probably can use some library to inject an env var like CLOJURE_ENV (method I learned from node.js land) and use that for the branching logics. But I'm wondering since lein knows about my profiles already (dev, test, uberjar<-prod), can I somehow use that instead, or is there a better, more idiomatic way?

athomasoriginal 2020-10-11T17:20:29.170400Z

Good question. A little more background is this is CLJS. So my goal is to not have to do the “slurp + transform” at runtime. That’s why, in this case, I thought a macro would be best.

2020-10-11T17:38:56.170700Z

If icons in the expression (:icon-name icons) already has the value you want when the code is compiled, somehow, then likely you can use it in a macro invocation. If not, i.e. if the value of (:icon-name icons) cannot be known until run time, then there is no way to avoid the transformation at run time, that I can see.

practicalli-john 2020-10-11T17:46:22.170900Z

If it's just a couple of environment variables,you could just use System/getenv It there is quite a few, then aero is an excellent project https://github.com/juxt/aero

practicalli-john 2020-10-11T17:48:10.171200Z

Example of getting a port number https://practicalli.github.io/clojure-webapps/app-servers/basic-configuration.html

practicalli-john 2020-10-11T17:50:14.171600Z

Leiningen profiles are useful for running the app locally with a different environment

Day Bobby 2020-10-11T17:54:42.171800Z

hello there, first off thank you for your useful courses, I learned a lot from them. Funny you mentioned aero , it's a 🔥 library, I'm trying to take advantage of #profile (https://github.com/juxt/aero#profile). To use it I must pass the current environment into read-config call so thats really why I needed the solution.

Day Bobby 2020-10-11T17:55:31.172100Z

I guess i can just do System/getEnv to get the current profile from shell and use it there

Day Bobby 2020-10-11T17:56:42.172300Z

Thank you again!

athomasoriginal 2020-10-11T18:03:17.172500Z

Yeah, icons is a hardcoded map like

(def icons {:path-1 "path/to/blah.svg"}

2020-10-11T18:06:48.172700Z

There may be some differences between Clojure vs ClojureScript macros that I am not well prepared to answer, me being more familiar with using macros in Clojure, and I know there are some differences, or restriction in ClojureScript. For example, if that hardcoded map is defined in the same file as the macro is, then it isn't clear to me why it would not work. If the hard-coded map is in a .cljc file, and the macro is not, then you will likely need to find someone besides me to tell you whether that is possible.

👍 1
2020-10-11T18:08:37.172900Z

I think that perhaps when compiling ClojureScript, all macros are expanded in a Clojure/JVM process during compilation, which is why I am guessing that if the def of icons is in a cljc file, its value might not be 'visible' to the macro at compile time.

jumar 2020-10-11T18:10:33.173100Z

For JAR you want to enable socket REPL: https://clojure.org/reference/repl_and_main#_launching_a_socket_server

seancorfield 2020-10-11T19:52:58.173600Z

Note that if you enable "direct linking" as part of your compilation process when building your (uber) JAR, then you cannot easily "patch" the live, running app from a REPL (because you would also need to re-def every upstream call site of the function you change and, recursively, their callers all the way up to -main. If you are not using that, then you can re-def functions on the fly in production via a REPL and the change take immediate effect. We do this in production for one of our apps where we do not want any downtime due to redeployment (and for complex reasons it is a single instance).

seancorfield 2020-10-11T19:53:55.173800Z

Also bear in mind that such changes only stay in affect until the next app restart (unless you also update the source and/or JAR file with the same changes applied via the REPL).

athomasoriginal 2020-10-11T19:58:22.174Z

Indeed. After a little more research, I found mfikes posts (in case a future person is trying to do something similar) which sheds some light on the issue: https://blog.fikesfarm.com/posts/2016-01-05-clojurescript-macros-calling-functions.html https://blog.fikesfarm.com/posts/2015-09-07-messing-with-macros-at-the-repl.html

2020-10-11T20:26:29.174200Z

Mike is definitely a person who should know the ClojureScript compiler and its effect on how macros are processed in ClojureScript very well.

👍 1
💯 1