Fork me on GitHub
#beginners
<
2022-08-15
>
quan xing02:08:52

How can I redirect (System/out) when I use println print something to other place like file or UI output window Or redirect clojure.tools.logging 's log/info

ahungry03:08:52

Maybe *out* ?

Alex Miller (Clojure team)03:08:02

those may be a bit misleading. The Clojure print functions will print to clojure.core/out

Alex Miller (Clojure team)03:08:37

so you can bind out to some other stream around the print functions

(binding [*out* *err*] 
  (println {:hi :there}))
will for example bind out to err during the print call (printing to stderr instead)

Alex Miller (Clojure team)03:08:04

if you want to bind to a file stream...

(require '[ :as jio])

(let [f (jio/file "log")]
  (with-open [fos (jio/writer f)]
    (binding [*out* fos]
      (println {:hi :there}))))

Alex Miller (Clojure team)03:08:47

and sometimes it's useful just to dump a string to file (see spit), or to print to a string, then put it somewhere else (see pr-str or print-str ).

quan xing04:08:01

can I bind out in global ? and than all call println will print to file? if I want to print UI , should I define self MyPrintStream extends PrintStream and than overwrite print method to print string into the output window's textfield? (binding [**out** myPrintStream]

Alex Miller (Clojure team)04:08:35

bindings have per-thread values. there is a root value for the out var and you could alter it directly (with alter-var-root), but then any print from any thread would go to the rebound out and this can be a pretty frustrating way to work (since that includes repl outs etc). this is not typically done. it would be more common to make a function that rebound out just in the function scope to print to the desired location, or format the log message to a string, then .write directly to a log stream

Alex Miller (Clojure team)04:08:16

for text fields, they typically want a string, not a stream, so you're better off printing to a string, then setting the text

Alex Miller (Clojure team)04:08:46

in both cases, you may also want some kind of queue in front of printing so you're not blocked waiting to print

Alex Miller (Clojure team)04:08:26

most clojure log libs have the ability to take care of all this for you

quan xing04:08:05

Thanks. Yes I want to redirect log output message to the UI, But I don't know how to do it There are no examples

Alex Miller (Clojure team)04:08:35

That will probably depend on which logger and which ui

quan xing06:08:27

I used to the log4j and UI used swing JTextArea with seeaw. I define the callback function in core function like this :

(defn dosometing [log-fn]
    .....
    (log/info "lala")
    (log-fn "lala")
)
and then In the UI side :
(dosometing (fn[e] (log-to-textarea))
This is my reslove solution. I want (log/info ) to the textarea not use the callback function

quan xing08:08:39

how can I generate replace into table valeus use honneysql

quan xing09:08:18

How to get original define map (def p {:b 1 :a 2} ) by original sorted

andy.fingerhut13:08:43

The built-in implementation of Clojure maps uses a memory-efficient "array map" that takes linear time to do lookups, but only up to a small limited number of key/values pairs, I believe 8. For larger maps, it switches to using hash maps, where (seq my-map) returns key/value pairs in the order of the hash value of the key, which is almost never the order that they were added originally.

andy.fingerhut13:08:54

There are other implementations of maps, not built into Clojure, but available in separate libraries, that preserve the order that keys were added to the map. ordered-map, for example, in its implementation, maintains a regular Clojure map for fast hash-based lookup, but separately keeps a vector that preserves the order that the keys were added to the map: https://github.com/clj-commons/ordered

andy.fingerhut13:08:17

However, note that if you use the ordered-map library, or nearly any other non-built-in library with a different map implementation, there are still many Clojure core functions that return maps with Clojure's default built-in implementation, and using any such function will thus return maps that have "forgotten" the original key insertion order.

andy.fingerhut13:08:49

Feel free to ask more about your particular use case for why you want the original sorted order of the key/value pairs, since then someone may have more specific advice on how to handle it.

👍 1
kennytilton13:08:19

@U0CMVHBL2 wrote: "using any such function will thus return maps that have "forgotten" the original key insertion order" Great point ^^^. Kinda like swimming upstream. 🏊 If the order is meaningful, perhaps it could or even should be made explicit as a new attribute. 🤷

andy.fingerhut14:08:46

Examples of Clojure core functions that return Clojure built-in maps, even if you pass into them as parameters a custom map type: select-keys, update-keys (sometimes update-vals, depending upon whether the map you give it implements transients or not). This is NOT intended to be a complete list, just a quick check of a few Clojure core functions that return maps. If you see a base case of {} in some implementation that is built up into a return value, that is a pretty good sign that it will return a Clojure built-in map, rather than a custom map type you provide as a parameter.

andy.fingerhut14:08:03

If you see a return value that is created from the parameter(s) via only operations such as assoc, that is a likely sign that the map returned will be the same custom type you gave it, since all custom maps must implement assoc

Kamuela14:08:40

Is there a common utility to deal with something like a bunch of predicate? functions and whether or not they're all true? Basically like an and operation that just takes in a bunch of predicates

Alistair Bull14:08:32

every-pred might be what you're after?

👍 3
Kamuela15:08:24

Wow! Thank you!

brianwitte15:08:39

Hello, there! I have recently spent some time digging into the Clojure gRPC story because I felt like putting it into a bot of mine (completely unnecessary, but why not?! 😄 ) to try some different things out. There a few resources out there for getting started, but most are somewhat dated. Would folks be interested in a repo with a minimal setup in it for exploring gRPC in Clojure? I can carve what I have out and clean it up a bit, put it in a repo, and write a little bit in the readme if folks are interested.

Ben Sless16:08:35

I can't un-recommend it enough. Protobuf is just wrong, never mind rpc being a mess If this still doesn't convince you check out a library like appsflyer/pronto for a decent-ish API to protocol buffers

brianwitte16:08:42

Sheesh, ok. I kinda liked the idea of just “calling functions” from a client. My bot is called from a golang service

Ben Sless16:08:59

There's a pretty strong aversion to rpc in the clojure world, I'm just being unoriginal (that after implementing and scrapping to rpc libraries)

🤝 1
Ben Sless16:08:27

calling a function in another place is a nice way of making a distributed object

💯 1
Ben Sless16:08:50

how about implementing it more as cqrs?

brianwitte16:08:23

right, yes. I can see why modeling things that way would be more the Clojure way. I will be honest, I have passing familarity with what CQRS is via a rails gem I used in the past and playing around with elixir but by no means understand it well or why its good. I guess I have some homework to do!

Ben Sless16:08:04

you can send commands as data, way cooler and clojure-y-er than invoking a remote method

✔️ 1
didibus17:08:48

I don't think there's anything wrong with RPC, but gRPC is a bit more annoying because it isn't self-describing.

didibus17:08:55

What I would do is use a Java gRPC client, and just use that from Clojure. Still super easy, just that yes, Clojure tend to prefer data, and data is self-describing, but if you've got a GO server you want to call, nothing wrong with that.

brianwitte18:08:09

Right. It's the other way around. Effectively, I have a bot/server that does a bunch of stuff that is written in golang. I want to write a bunch of new functionality in Clojure but keep the golang part in place. gRPC seemed like a way to write the bot with two languages in a more deeply integrated style. This is mostly for learning Clojure and doing this in an idiomatic Clojure way is preferable. So mostly I am trying to get Go code and Clojure code to integrate well together. Right now the Clojure code does not have its own database (since it is not persisting data yet), but it will.

didibus18:08:58

I see, so you want to expose API endpoints from the Clojure code so that golang calls them? And thought of exposing gRPC APIs for that?

brianwitte18:08:44

Yes, correct

didibus18:08:04

Ok well, ya that's a lot more annoying haha. There's no official Clojure support from Google for protoc to generate a Clojure server. I'm not sure if someone maintained their own well maintained and up to date. I found this: https://protojure.readthedocs.io/en/latest/ which seems to have commits from this year, but it says Ring and Compojure support isn't included, only Pedestal. So like serving gRPC I'd say is not well supported in Clojure. If I really had to do it, I would probably generate a Java server with protoc, and wrap my gRPC endpoint in Java shims that delegates to Clojure functions for their implementation.

didibus18:08:29

But you'll probably be better served with just exposing some standard REST APIs from Clojure and using those from Go. I'm sure it's relatively straightforward as well from go to call a Rest API, probably easier to call a Rest API from Go then to serve a gRPC endpoint from Clojure.

brianwitte19:08:55

Right, yes. Haha

brianwitte19:08:32

I should prob put this book away for a bit

brianwitte19:08:11

But in all seriousness, this was for learning so it def seems like learning something else with Clojure would be time better spent

didibus19:08:52

Ya, I would say, go with gRPC if you have to because you need to integrate with a bunch of micro-services that all use gRPC to talk to each other already and all that, but its going a bit out of the way with Java shims, not hard, but a bit not as fun from a Clojure point of view.

💯 1
Stuart18:08:40

Hi, I'm working on a codewars problem, the problem is to evaluate RPN. I have the code working that solves the problem but I'm failing the tests, e.g.

(deftest reverse-notation
  (is (= (calc "") 0))
  (is (= (calc "3") 3))
  (is (= (calc "3.5") 3.5))
  (is (= (calc "1 3 +") 4))
  (is (= (calc "1 3 *") 3))
  (is (= (calc "1 3 -") -2))
  (is (= (calc "4 2 /") 2)))
Problem is my result is a double (since 3.5 is a double), and the tests expect sometimes a double and sometimes an int. Is this just a known way to make this work, I'm thinking I will have to divide by 1 and check for remainder, and if no remainder cast it to an int. This seems hacky and there must be a better way?

hiredman18:08:28

I think this is a bad test, and what you are doing is likely correct

hiredman18:08:53

my guess is the writer of the test expects you do use something like read-string to parse numbers

Stuart18:08:21

I did this to make the tests pass

(defn convert-result [n]
  (if (zero? (mod n 1)) (int n) n))

(defn calc [expr]
  (if (= "" expr)
    0
    (->> (str/split expr #" ")
         (map tokenize)
         (evaluate-rpn)
         (convert-result))))

hiredman18:08:23

and if you use read-string and clojures operators you will get the behavior expected in the test

Stuart18:08:07

I thought read string was bad, as it could execute code ?

hiredman18:08:21

read (or read-string) will return an int when reading 1 and a double when reading 1.0, and those propagate through +, /, etc

hiredman18:08:04

it is complicated, I generally avoid using read unless I am specifically reading edn

hiredman18:08:18

there is clojure.edn which is "safer" versions of read and such

hiredman18:08:57

but that is part of the reason I think it is a bad test, it leads to do the (easier) wrong thing

William LaFrance18:08:08

Can you go the other way and have your calculator only support floats?

Stuart18:08:48

floats still fail the tests as it expects -2 and I'm giving it -2.0 for example, or it expects 3 and my result is 3.0

Stuart19:08:31

Hi, I've written minesweeper in clojurescript and re-frame. https://github.com/stuartstein777/cljs-minesweeper I'd really appreciate if someone who knows re-frame and CLJS could have a quick glance and tell me what things I'm doing that are wrong. It works (to the best of my limited testing). But I don't know if I'm doing the re-frame stuff correctly or not. I don't have a lot of experience with re-frame at all (or front end at all). I mean, I'm using svg to show numbers because I couldn't get the css to work to scale based on board size, but that's an aside). If anyone could look and say "no, dummy you are doing foo wrong, you need to read bar about how to do foo properly" I'd be really grateful! Don't worry about hurting my feelings if its all just not clojurey at all 🙂 I'll take it all as a learning experience.

sheluchin21:08:10

What can I read to understand the convention of *.impl namespaces?

Alex Miller (Clojure team)21:08:45

I think the convention is just that impl means implementation and not public api

Alex Miller (Clojure team)21:08:16

so as a consumer of a library, you should not be calling those

sheluchin21:08:00

Ah, okay. There's not much more to it than that? Is it a convention from Java or just a Clojure thing?

Alex Miller (Clojure team)22:08:26

since namespaces and vars are inherently available, this is a convention to mark "public"-ness

sheluchin22:08:48

Clojure's encapsulation convention, I guess you could say... And it looks like there is no particular mapping between "public" and impl namespaces? It's pretty much organized however?

seancorfield02:08:52

@UPWHQK562 Sometimes the "implementation" leaks out in libraries, such as tools.logging which expects you to know about and use the various logger factory functions in its .impl namespace https://github.com/clojure/tools.logging#selecting-a-logging-implementation (I do not like this).

sheluchin13:08:31

@U04V70XH6 Thanks for pointing that out. It makes sense given it's just a convention without any enforcement by the compiler or runtime.