Fork me on GitHub
#beginners
<
2021-07-21
>
kozmicluis00:07:37

I'm doing a project in cljs with reagent and I want to put a view in a folder called "views", if my core namespace is myapp.core what should the namespace of the views inside that folder be? myapp.views.someview ?

WWeeks00:07:04

@seancorfield yes that is perfect. Sorry I did not word the question well. I want have Clojure get data, turn the data into a string that looks alot like a powershell script, then run the powershell script.

seancorfield00:07:24

@wme.weeks OK, so the part you're stuck on is just running Powershell from Java/Clojure once you've produced the script?

seancorfield00:07:16

Take a look at the clojure.java.shell/sh function: https://clojuredocs.org/clojure.java.shell/sh

seancorfield00:07:55

I would expect you can run a command like "powershell" "scriptname" with that...?

WWeeks00:07:28

Yes That is what I would like to do. thank you so much

seancorfield00:07:34

(I don't use Powershell so I've no idea if that would work -- I only use WSL2 on Windows so everything is Linux for me)

RIchard Bowen02:07:04

Hey. Do any of you participate in any online Hangouts groups (or the like) meetups for Clojure/ClojureScript learning and practice? If so I'd like to participate 🙂

sova-soars-the-sora04:07:01

If there isn't one we oughta start one @rbowen

zackteo08:07:02

Hello, if looping through my data with a for is it possible to get the index of which item it is at the moment?

delaguardo08:07:46

no, but you can prepare you data to carry an index:

(for [[i el] (map-indexed vector data)]
  )

zackteo09:07:40

Thank you! I was looking at that but was a bit unsure of how I might use it

Lucy Wang09:07:46

Is this a legitimate hack in CLJS that a record could have any method defined by defining them on Object without creating a protocol first?

(defrecord R1 []
  Object
  (f1 [this]
    (js/console.log "R1.f1")))

(.f1 (map->R1 {})) ;; prints "R1.f1"
This code works in cljs, but not in clj

markgdawson12:07:04

Can anyone help me with how I can refer to the original implementation of a function when I use with-redefs? I want to change the behaviour of a function, but only when the input matches a predicate. Otherwise I want to delegate to the original behaviour.

noisesmith13:07:22

with-redefs is not safe to use in application code, and easy to produce race conditions will lose the base definition

👍 3
markgdawson09:07:32

Yeah, we don't use it in application code. It's a bit of a hack that we've been using in some debugging tooling. 🙂

markgdawson12:07:08

I've tried holding on to the original reference with (def orig-myfn (deref #'some-namespace/myfn)) and then calling (orig-myfn ...) from inside the definition I'm using to replace. But that didn't seem to work.

ghadi12:07:24

You’ve got the right idea, just need to do it in a let binding, and close over it in the replacing definition: `(let [old the-var] (with-redefs [the-var (fn […] use or call old)] ….)`

markgdawson12:07:56

Thanks @ghadi, it still isn't working for me, let me see if I can set up a more minimal working (or not working) example and I'll post it here.

walterl12:07:06

This does (and demonstrates) what you want, but I'm not sure how idiomatic it is

ghadi12:07:34

Keep in mind that with-redefs is often a smell that your program is missing an argument

markgdawson12:07:07

This seems to work too, just as @ghadi suggested:

(let [println-orig (deref #'println)]
  (with-redefs [clojure.core/println (fn [s]
                                        (println-orig (str "Hello, " s "!")))]
    (println "World")))
So it's probably something I'm doing wrong in the bigger example.

ghadi12:07:31

(The function that you’re redefining should be passed to the thing that calls it, not directly wired)

walterl12:07:19

@UDC7GA4QG side note: (= foo (deref #'foo)) => true

markgdawson12:07:10

Yeah, that's a good point. I don't need the deref there.

jaju12:07:45

On the topic of with-redefs - what am I doing wrong here? I can’t seem to use redef with +/-

user=> (with-redefs [- +] (- 10 20))
-10
user=> (with-redefs [+ -] (+ 10 20))
30
user=> (with-redefs [map reduce] (map + (range 10)))
45 ;; okay

ghadi12:07:37

Certain functions in clojure core are handled specially

ghadi12:07:42

For performance

jaju12:07:41

Thanks @ghadi - I looked at the source for +, -, map, reduce - is there something I can read more about to understand?

jaju12:07:37

For example, I noticed use of reduc1 - does it come in the way?

ghadi12:07:59

There are some :inline annotations (which are not for public use) that replace calls to vars with calls to static methods on clojure.lang.RT

💡 2
ghadi12:07:27

Like on some primitive math functions

jaju12:07:32

Interesting - I’d have imagined they get bound to later in the execution - so, redef gets a chance to override. But, is this behaviour to be expected? I am still surprised and unclear.

markgdawson12:07:43

OK, I think some of the issue I was seeing was that I was rebinding a multi-method. And foolishly only bindings one of the arities. That worked when I didn't have to refer to the original function, but doesn't work now. Not sure why. Anyway, using a variable arity function to override seem to do the trick. 🙂

markgdawson12:07:16

Thanks @ghadi and @clojurians-slack100 for helping me out on that one.

👍 2
ghadi13:07:17

I would categorically say that redeffing clojure.core is undefined behavior @jaju There are other things that confound redef (clojure.core is shipped direct linked), and redeffing clojure.core functions has the potential to break clojure itself

👍 2
jaju13:07:31

Agree. I’ve never used with-redefs. But I guess there should be a clean denial from Clojure when overriding core, rather than a silent failure. Thanks so much for explaining - I was struggling to identify where I was going wrong! 🙏

jaju13:07:41

Is it safe to inspect the meta of a var and look for :inline to help decide that with-redefs will not work?

ghadi14:07:17

not sufficient

ghadi14:07:40

consider 99% of clojure core functions un-redeffable in the general case

👍 2
zackteo13:07:17

I am wrapping shell/sh in a function. Is there a way to pass in optional arguments. My issue is that shell/sh seems to only take strings outside of any data structure

noisesmith13:07:40

if I understand you correctly, the solution is to use apply

noisesmith13:07:37

(apply f [a b c]) and (apply f a [b c]) and (f a b c) all result in the same function call

zackteo13:07:56

Yeap I think you are right!

Cora (she/her)13:07:22

I never noticed how strange that API is

Cora (she/her)13:07:16

I mean, for a core API

noisesmith13:07:48

without the varargs you end up with a bunch of boilerplate collection building code

noisesmith13:07:59

or silliness with partial

noisesmith13:07:20

(I've dealt with this from coworkers who didn't realize apply was varargs)

zackteo13:07:04

On that same note, How should I define a function with defn if I have optional arguments? (defn optimise {a b & opts]) what should opts be ? a hashmap?

noisesmith13:07:29

opts ends up being effectively a list

noisesmith13:07:15

(ins)user=> (defn optimise [a b & opts] (println "opts is" opts))
#'user/optimise
(ins)user=> (optimise 1 2 3 4 5 6)
opts is (3 4 5 6)
nil

zackteo13:07:25

If have a list of named optional args is the & opts method a good one? or should just do (defn optimise [a b opts]) where i just put opts as {} if there are no optional args?

noisesmith13:07:20

if you expect key/value pairs using a hash map instead of varargs is usually better

zackteo13:07:34

For context am wrapping a script with shell/sh and there are compulsory and optional arguments. And am thinking what is the best way to approach this

noisesmith13:07:24

you can have a separate arg list for the case where no opts are provided

(defn foo
  ([x y] (foo x y {})
  ([x y opts] ....))

zackteo13:07:50

Right 😅 thanks I totally forgot about that

noisesmith14:07:34

since this is #beginners - a useful idiom to know:

(merge {:a "default-a" :b "default-b"} opts)
merge effectively implements everything you need for merging defaults with provided options

2
noisesmith14:07:02

the second arg overwrites any keys shared with the first arg

Cora (she/her)14:07:44

when I said it was a strange API I meant in the wild I typically see two arities in a case like this, one with a single arg - cmd (vec of strings to execute), which then calls the same function with the same cmd with an empty options hashmap. second arity takes cmd and a hashmap of options

Cora (she/her)14:07:50

args are [cmd] and [cmd opts] (or even flip the order on the second one)

Cora (she/her)14:07:16

you can see an example of both of those right here! two arities, with just cmd for the first and then cmd and opts for the second, and then merging over defaults

seancorfield15:07:23

Also worth noting that as of Clojure 1.11 (in alpha right now), if you declare (defn foo [x y & {:as opts}] ...) then you can call this with either key/value arguments (foo 1 2 :bar 3 :quux 4) or a hash map (foo 1 2 {:bar 3 :quux 4}) (and even a mix of the two, with the hash map following any key/value arguments (foo 1 2 :bar 3 {:quux 4 :wibble 5})

😮 2
😲 2
seancorfield15:07:40

(over time we will probably see more APIs declared this was, as opposed to [x y opts], once we have the flexibility to call them in "human style" -- key/value named arguments -- or in "program style" -- a hash map of arguments; you can already do this with the CLI with -X and the new -T options)

ChillPillzKillzBillz16:07:10

Anybody know how to format strings like printf in C? ... you know with the %d arguments... I only found the [goog.string :as gstring] which has a format function which lets me do exactly that... but is there something similar in clojure?

tws16:07:55

https://clojuredocs.org/clojure.core/format. or cl-format if you really want to go crazy. there’s several pages of docs for that one.

ChillPillzKillzBillz16:07:28

dunno I never found it

ChillPillzKillzBillz21:07:41

Hey this doesn't work in clojurescript... is that true?

hiredman18:07:07

format and printf are both built on java's formatting, if you are using goog.string you don't have java stuff because you are writing clojurescript

sova-soars-the-sora20:07:25

Hello, what am I missing about this function?

(defn rapunzel [rpnzls]
 ([] (rapunzel "rapunzel"))
 ([rpnzls]
  (if (= rpnzls "rapunzel") 
   (println "let down your hair"))))
I get a StackOverflow xD

phronmophobic20:07:50

(defn rapunzel [rpnzls] ;; <<<------- what is this?
 ([] (rapunzel "rapunzel"))
 ([rpnzls]
  (if (= rpnzls "rapunzel") 
   (println "let down your hair"))))

sova-soars-the-sora20:07:29

Thank you 😄!

😁 2
Tomas Brejla21:07:26

ironically, vector can be used as a function (thus no runtime error due to ([] ....) ), so the code did what you asked it to do 🙂

Jeroen van Wijgerden21:07:14

For a research project I need to perform a long calculation (multiple hours) resulting in a large map (~ 50 million keys). My goal is to do the calculation only once on someone else's very powerful machine and somehow store the resulting value such that I can use the value on my own machine machine without having to wait those long hours ever again. The direction I went in is to make a second project where I assign the result of the long calculation to a var, AOT compile it and include the resulting jar as a dependency in my research project. To test this approach I did the following. In my to-be-compiled library project I have a src/lib/core.clj:

(ns lib.core)

(def x (do (println "calculating...")
           :foo))
in my project.clj (I use Leiningen 2.9.6 on Java 11.0.11 OpenJDK 64-Bit Server VM) I include :aot :all and indeed, when I lein jar I see
Compiling lib.core...
calculating...
and I obtain a .jar file. I successfully add it as a dependency in my research project, but when I therein start a REPL and evaluate
(ns research.core
  (:require [lib.core]))
to my surprise I again see calculating... being printed. If I do not include :aot :all in the library's project.clj, upon lein jar I do not see calculating... printed, so the :aot :all does seem to have effect. Seeing calculating... again when I evaluate the namespace that requires the .jar makes no sense to me: isn't the jar AOT compiled? Am I missing something? Also open to other ways of reaching my goal of calculating once and having the value available on other machines/REPL restart.

ghadi21:07:56

When you AOT a namespace, it means that the compiler is going to write bytecode to disk. That bytecode contains the recipe for loading your namespace. It is not writing bytecode that represents the state of the namespace after the load.

jumar06:08:54

About the "recipe for loading your namespace"

jumar06:08:50

@ghadi if it's just a recipe why is it then evaluating the top-level forms/vars? Is this the "just recipe" part also the reason why the code is evaluated, again, when loading the AOT-compiled namespace.

ghadi11:08:19

It loads the recipe and remembers the recipe at the same time

ghadi21:07:37

What you want to do is compute your expensive thing, then serialize map to disk using something like EDN, JSON, or Transit.