Fork me on GitHub
#beginners
<
2020-03-10
>
Lucas Vassalli03:03:08

Hello, may I have your opinions on my solution for accessing tabular data by indices?

Lucas Vassalli03:03:44

I'm representing the tables as multi-dimension arrays

Lucas Vassalli03:03:31

They're created once and only accessed then

Lucas Vassalli03:03:14

They have various dimensions

Lucas Vassalli03:03:31

`(defmacro lookup [table &amp; idx]` `(conj (for [i idx] `(nth ~(dec i))) `~table '-&gt;))`

Lucas Vassalli03:03:20

I've managed to get it to work, just wonder if it's become some aberration. How would you do this?

hindol03:03:39

You don't need a macro for simple things like this.

jsn03:03:42

why would you do that? why not just use`get-in` ?

jumpnbrownweasel03:03:29

@UTQEPUEH4 if I understand your comment, you're also implying that vectors should be used, so that direct indexing can be used. Right?

jsn03:03:12

the vectors already are used, and yes, i'm saying that there already is a built-in function doing more or less the same

Lucas Vassalli03:03:19

get-in sounds great. I figure I can just map dec on my indexes and apply get-in

Lucas Vassalli03:03:10

@UTQEPUEH4 I somehow ended with a macro after trying to write every lookup manually using threading macros

Lucas Vassalli03:03:17

I realized I was doing repetitive tasks and decided to just turn the basic structure into a macro.

jsn03:03:46

why a macro, not a function?

Lucas Vassalli03:03:47

Any thoughts on using nested vectors for index acessing? I've got like 20 tables, some 100 items each

Lucas Vassalli03:03:05

Am I missing some trivial manner to transform this macro into a function?

jsn03:03:18

well, your macro isn't very trivial to begin with (macros usually aren't), and get-in is basically (`reduce nth table idxs)`

jsn03:03:05

or a simple recur loop, if it's easier to understand

Lucas Vassalli03:03:06

Something like (reduce nth table (map dec idx)) should do

Lucas Vassalli03:03:55

Thank you for your suggestions

Lucas Vassalli03:03:21

There were obviously better solutions I just wasn't seeing

hindol05:03:00

Instead of (map dec idx), did you consider adding a dummy element at the beginning to each vector in your table?

didibus05:03:55

Is it just that you want to be able to lookup by a 1-based index?

didibus05:03:03

You don't need a macro for that, can just do:

(defn lookup [table & idx]
  (get-in table (mapv dec idx)))

didibus05:03:39

If its just that you want a get-in that is var-arg instead of taking a vector, you can just do:

(defn lookup [table & idx]
  (get-in table idx))

didibus05:03:23

Otherwise like other have said, get-in already does that:

(get-in [[1 2 3] [4 5] [6]] [0 1])
;;=> 2

teodorlu13:03:45

If you hit pain with your current hand-rolled tables, here are two libraries that might prove useful: https://github.com/redplanetlabs/specter (flexible handling of nested data) https://github.com/techascent/tech.ml.dataset (really fast ND-arrays)

Lucas Vassalli13:03:40

@U0K064KQV It's the first case. I'm basically using your suggestion, thanks. @U3X7174KS They don't seem suitable for this but are definitely interesting, thank you.

FiVo09:03:45

If I have done an extend-type or extend-protocolis there a way to remove those again and go back to the standard definition?

hindol10:03:51

You mean, in a running REPL?

teodorlu12:03:21

Options: 1. Re-run the type definition (deftype/defprotocol) 2. Use something like clojure.tools.namespace.repl/refresh or integrant.repl/refresh If you're using CIDER; there's M-x cider-ns-refresh.

teodorlu12:03:54

I'm not aware of a way to remove specifically the "registration" for the type you extended with.

Old account15:03:45

Is there a function which wraps another function and can ammend it's arguments when called.

thom15:03:07

Not really, other than fnil (which can replace nils in supplied arguments with default values). Trivial to write it yourself though, depending on what sort of amends you need to make.

Old account15:03:00

some of arguments are guaranteed to be strings so I want to prefix then with astring

Lucas Vassalli15:03:49

How about (defn f-wrapper [arg] (f (str ammend arg)))

thom15:03:48

As I say, don't think there's a built in way to generalise this problem, although you could write one.

โœ”๏ธ 4
Mattias15:03:39

Hey. easiest way to interact with REST (well, Json over http) endpoints? Preferably without dependencies... โ€œ(rest...โ€ didnโ€™t work. Hopless term to search for. ๐Ÿ˜Š

jba16:03:15

the easy way is https://github.com/dakrone/clj-http For a quick and dirty you can also use slurp: https://clojuredocs.org/clojure.core/slurp

Mattias16:03:54

Thanks! ๐Ÿ‘

hindol16:03:30

Note that you will also need to pull in the optional Cheshire dependency if you want automatic JSON and Clojure map conversation, or you can do that outside the library.

Mattias16:03:19

Noted ๐Ÿ˜„

didibus21:03:23

Hum... Probably @UJRDALZA5's recommendation is best. But since you mentioned without dependencies, Java 11 includes a bundled HTTP Client: https://openjdk.java.net/groups/net/httpclient/intro.html

didibus21:03:30

Which you can use from Clojure using interop

Grigory Shepelev16:03:50

Good day! How can I pass "unrealized" function with some argument as argument of other function? Say

(def f [x f1] (* x (call f1))))

(def g [x y z] (+ x y z))

(def h [] (f 3 (g 1 2 3)))
I want (g 1 2 3) to be called EXACTLY when it's called in f as argument f1. Not when it was passed as parameter.

jumar16:03:15

You need a macro for that - ordinary function calls always evaluate their args before control flow is passed to the function

Grigory Shepelev16:03:15

I though future/delay/promises will work somehow. But can't find out

hindol16:03:43

That's because many of those are macros.

markmarkmark16:03:17

you could pass #(g 1 2 3) and then just call it as a function in f

๐Ÿ‘ 12
didibus16:03:41

There's three way to do it. Others already mentioned two of them. You can use macros. You can use thunks (aka higher order functions). And the last way is to use eval on quoted forms.

๐Ÿ‘ 4
didibus16:03:20

My recommendation here would be to use higher order functions

didibus16:03:02

(defn f [x f1] (* x (f1)))
(defn g [x y z] (+ x y z))
(defn h [] (f 3 #(g 1 2 3)))

8
didibus16:03:26

This is called a thunk, which is an anonymous function of zero-arity, which is used simply to delay evaluation and be passed around to something which is expected to evaluate it later.

didibus16:03:31

You can also use a delay to create thunks. They're slightly different to anonymous function thunks in that they'll cache the result, so you need to be sure you will not need to re-evaluate it more then once.

didibus16:03:03

(defn f [x f1] (* x @f1))
(defn g [x y z] (+ x y z))
(defn h [] (f 3 (delay (g 1 2 3))))

didibus17:03:24

Also, in Clojure code, you might see people using partial to create anonymous function thunks. It results in the same thing as using #() or (fn [] ...)

didibus17:03:51

(defn f [x f1] (* x (f1)))
(defn g [x y z] (+ x y z))
(defn h [] (f 3 (partial g 1 2 3)))

didibus17:03:35

Now let's see about using eval, I don't recommend it because eval has more edge cases, but here you go:

(defn f [x f1] (* x (eval f1)))
(defn g [x y z] (+ x y z))
(defn h [] (f 3 `(g 1 2 3)))

didibus17:03:02

Finally, using a macro:

(defmacro f [x f1] `(* ~x ~f1))
(defn g [x y z] (+ x y z))
(defn h [] (f 3 (g 1 2 3)))

didibus17:03:23

In the macro's case here, x is also delayed till f evaluates

vinnyataide17:03:14

is this the correct way to read a bytearrayinputstream coming from a post body?

(let [_ (pp/pprint (slurp (:body request)))...

noisesmith17:03:21

this works if you want to block until the stream is done, and the stream is text and not eg. a jpeg

noisesmith17:03:59

I wouldn't expect pprint to do anything interesting to a stream that println wouldn't, and slurp always returns a string

vinnyataide17:03:26

oh yeah, that pprint is just a placeholder

vinnyataide17:03:49

what would be a nonblocking way? input-stream func?

vinnyataide17:03:05

just found the ring-json middleware, gonna try it

noisesmith17:03:43

the most generic thing is to process the inputstream byte by byte, but really if you know the kind of data you expect you can get a better choice - for json, you can use slurp just fine

noisesmith17:03:47

you probably want a json middleware eventually, but it seems like actually getting the data comes first

vinnyataide20:03:08

yeah the middleware worked great

vinnyataide17:03:22

Good afternoon!

tzzh17:03:08

Hi I am trying to redef a function using that function and that causes a stackoverflow, ie do something like

(with-redefs [f (fn [a b] (+ a (f b a)))]
...) 
what would be the best way to get around this ?

bfabry17:03:00

give it a different name using let first

bfabry17:03:53

(let [f-orig f]
  (with-redefs [f (fn [a b] (+ a (f-orig b a)))]
...))

tzzh17:03:22

ah sweet thanks :thumbsup:

hiredman17:03:59

you need capture the original value of f and call that

vinnyataide17:03:08

my request body is always coming empty, no matter the postman's request

tjb18:03:33

hey frens

tjb18:03:46

does anyone have reading material on when to use ^ in method names?

tjb18:03:27

> tjb just read the dang docs!

tjb18:03:32

๐Ÿ™‚

andy.fingerhut19:03:59

Definitely a good article to keep handy when learning Clojure and reading code written by others. Regarding your question, I do not ever recall seeing the ^ character used in a symbol that named something in Clojure. Some people do use unsupported characters in symbols that name things, because the Clojure code reader allows some unsupported things without throwing errors, partly for backwards compatibility with code out in the wild that used them, and partly because of the efficiency of checking all of the rules at compile and/or run time.

๐Ÿ‘ 4
tjb19:03:45

in #clj-kondo i saw this piece of code that triggered my curiousness as to โ€œwhy?โ€

tjb19:03:46

(defn ^:private ^ServiceActor conn-service-actor
  "Service actor from connection."
  [{:keys [::app-id
           ::app-service-token]}]
  (ServiceActor. app-id app-service-token))

andy.fingerhut19:03:06

Got it. I am so used to understanding what this use of ^ means that it did not even occur to me to think of the ^ character as part of the symbol. But I can imagine looking at it for the first time, and there it makes perfect sense that the ^ appears to be part of the symbol.

andy.fingerhut19:03:31

Fortunately, if you grok most or all of that 'weird and wonderful characters' article, and/or go back to it as needed, you should be pretty much done with understanding the syntax of Clojure.

๐Ÿ‘ 4
tjb19:03:30

so in this case does ^ signify anything to you (being a person who understands clojure more than i ๐Ÿ™‚)?

didibus19:03:40

That's a type hint

didibus20:03:20

It gets resolved to the meta {:tag type}

andy.fingerhut20:03:46

Right. And this section of that guide article has more examples and explanations, that are worth remembering exist in that article for if you forget: https://clojure.org/guides/weird_characters#_and_metadata

andy.fingerhut20:03:05

The main reason for such type hints in Clojure is to avoid slow calls to Java code. They can sometimes use the Java reflection API, if the Clojure compiler cannot determine at compile time which of several Java methods to use, instead determining on each call at run time which one to use.

didibus20:03:12

You have three typical meta scenarios, ^{} , ^: and ^

didibus20:03:05

Former you can give a full meta map, the middle is for a meta key with value true and the latter is for a :tag key with value to the type specified

didibus20:03:00

I think the hint here is in the wrong place. To type hint return value, pretty sure the meta has to be on the arg vector

didibus20:03:15

In this case, it's putting it on the fn var

didibus20:03:52

And that's wrong, because the Var does not hold a ServiceActor, it hold an AFunction

didibus20:03:34

So the var tag should be AFunction, but Clojure doesn't need hinting for that so it's not useful. And the return hint should be on the args vector

andy.fingerhut20:03:36

The Clojure compiler does in some cases use a type hint on the var itself, too, which can be useful if the value of the var is not a function.

andy.fingerhut20:03:55

I don't have comprehensive cases to quote you off the top of my head, but you will often see type hints before the var for some functions, but before the arg vector for other functions, and I think it is typically recommended to put it on the arg vector for a function, since that technique also works for functions defined with multiple arities.

andy.fingerhut20:03:05

Take a look at the source for the clojure.string namespace in Clojure itself, for plenty of examples of functions defined like this: (defn ^String reverse ..) . If that is wrong, then Stuart Halloway and Rich Hickey messed it up.

๐Ÿ‘ 4
tjb20:03:28

this has been super insightful

tjb20:03:36

thanks everyone for the lively feedback ๐Ÿ™‚

andy.fingerhut20:03:44

And here I thought I was going off into the weeds ๐Ÿ™‚

tjb20:03:45

this slack group has been so helpful in my learning

didibus20:03:46

Hum... It might be that Clojure also somehow leverages the var type hint, but it feels wrong to me

didibus20:03:10

But I could see the compiler trying to look at that as well

andy.fingerhut20:03:40

It can certainly be confusing, because there are two places where "it does what you need it to", so the rules for writing it aren't as concise as one might like. That is probably the source of the wrong feeling.

didibus20:03:50

Well, ya pretty much. But also it makes no sense

didibus20:03:09

Since the type of the value contained in the Var is Fn

didibus20:03:12

Not String

andy.fingerhut20:03:40

I have found that in most cases where as an engineer you get the feeling that where a software system ended up in a place that makes no sense to you, it makes much more sense if you knew the history of how it got there. Such history of decisions is often difficult to find, though.

andy.fingerhut20:03:38

"makes much more sense" perhaps a loose translation for "seems less insane" in some cases ๐Ÿ™‚

andy.fingerhut20:03:32

I am not here to convince you that this state of affairs makes sense. I am trying to explain what is.

andy.fingerhut20:03:19

For example, a type hint of Fn for a Var would be just about completely useless, yes? Very little, if any, Java interop calls would ever avoid reflection with such a type hint, because almost no Java methods care about class clojure.lang.Fn , or any other clojure.lang.* classes.

didibus20:03:52

Depends, if I'm trying to pass an Fn to a method

didibus20:03:08

And there's an overload for say FunctionalInterface

didibus20:03:16

Now I'd need to hint the Var

didibus20:03:42

I also feel I've tried to resolve type hints like that before and it didn't work

andy.fingerhut20:03:01

I suspect the decision might have been made because type hinting the return value of a Var that is a function is far more useful than saying "it is a function object".

didibus20:03:16

So I almost feel the hint on reverse might be wrong lol

didibus20:03:58

Maybe, I guess I'm just not convinced yet this is valid return value type hint

andy.fingerhut20:03:31

Try experiments of passing the return value of reverse to Java interop calls that can avoid reflection by knowing the return type is a String , versus my-reverse that does not have that type hint, and report back the results. I believe you will find that the type hint makes a difference in avoiding reflection in such cases.

didibus20:03:47

Ya I'll try in a REPL

didibus20:03:21

The compiler might have special logic. But the official docs do not document type hints at that place

andy.fingerhut20:03:11

In particular this text: "`:tag` a symbol naming a class or a Class object that indicates the Java type of the object in the var, or its return value if the object is a fn."

didibus20:03:02

That's interesting

didibus20:03:54

So it does have special handling if the real type of Var is fn

didibus20:03:53

I guess I'd be out of luck in my contrived example then :p

andy.fingerhut20:03:00

You can still use a let-bound symbol with a type tag in that case, I would think.

andy.fingerhut20:03:36

but I'm not so curious right now that I want to create code that would answer that question. let-bound symbols with type tags are a useful technique for avoiding reflection in some cases, though, if you can't easily find another way.

didibus20:03:14

Ya me neither :p

didibus20:03:41

I did learn something though. So it seems you can type hint the return on two places

didibus20:03:44

Both valid

didibus20:03:57

Not sure which one has precedence

didibus20:03:15

And if the above would work to hint all arities

andy.fingerhut20:03:14

There are a few functions in Clojure source code that have type hint before the Var name and on the arg vector. The only ones I have seen (e.g. chunk-next) have the same type hint in both places. I don't know why that code does that, nor have I tried to give different types in the two places. That case is, I doubt, explained in the official docs anywhere, nor would I expect it ever to be (my personal guess -- I have no inside knowledge what the plans are for the official docs).

andy.fingerhut21:03:54

I would not be surprised if code like that might even date back to an early time in Clojure's implementation when type hints were supported in one place, but not yet the other, and support was being added for "the second place" in the compiler around that time. Just a wild guess, though. That code was written by the compiler writer, who knew exactly what it could/could not do.

๐Ÿ‘ 4
didibus21:03:31

My guess, is that if you pass the function around, you no longer have access to the var hint, but you maybe still have access to the args vector hint?

didibus21:03:29

Though... I think that's also wrong, since even the arglist meta is on the Var

didibus21:03:59

So I dunno

andy.fingerhut21:03:56

It is an area I delved into deeply once when writing some warning checkers in Eastwood, and wrote a section of its README explaining some of it, but not everything you are asking about, which I don't know the answer to all of them.

andy.fingerhut21:03:50

The compiler source code is there for the curious -- everywhere is walking place if you have the time ๐Ÿ™‚

andy.fingerhut21:03:22

Everywhere except a 100% certain answer to the question of "what was person X thinking when they wrote this?", which is unusual to find the answer to, unless they were doing insanely detailed literate programming, or you find the answer in some on-line discussion somewhere. I imagine it isn't exactly fun to answer such questions indefinitely to all askers.

didibus21:03:13

Ya, I think people ask, because we like to know what is a wart versus what has a great reason behind it that we're just not seeing yet.

didibus21:03:09

Like my curiosity would end, if someone was just like... Ya, this is a historic wart. But until then, I'm thinking, oh, are there times where one should be used over the other? Limitations to each, etc.

andy.fingerhut21:03:57

Java primitive type hints only work on arg vector, I am pretty sure.

andy.fingerhut21:03:31

if you want the implementation to take advantage of passing actual Java primitive long or double types as parameters or return values.

andy.fingerhut21:03:48

Except for that, it isn't clear to me that either is a historic wart.

didibus21:03:17

I mean, it seems like the argvector one is just superior in every way

didibus21:03:45

I wouldn't really understand why they would have added the other if they started with that one, unless too many people kept making the mistake and they thought well... lets just support both

didibus21:03:37

But my total guess out of thin air is that probably they started with the var one, and things evolved where they needed to support multi-arity, and other scenarios and maybe added the argvector one as well

didibus22:03:03

On main Clojure channel, Alex brought up a good difference. Its what you just said, but it explains why. The var meta is evaluated, so typing ^long will resolve to the long function. Seems argvector is now preferred.

andy.fingerhut22:03:38

That is true, but I was trying to describe yet another difference: you can cause the Clojure compiler to create JVM byte code for a method that returns a primitive long or double type if you use the appropriate type hint on the arg vector. You cannot do that by putting any kind of type hint of any kind on the Var

andy.fingerhut22:03:22

At least, I am pretty sure that is true. It could be just one more reason to prefer type hints on the arg vector for functions.

didibus22:03:27

nice to know

spasov20:03:07

Where can I check what's available to me when using the for sequence comprehension form? More specifically, I'm referring to the :when part:

(time (dorun (for [x (range 1000) y (range 10000) :when (> x y)] [x y])))

spasov20:03:46

I've seen also :while and :where, but I'm not sure how to search the docs for that particular thing.

spasov20:03:50

Lol, trust me or not, I had that page open while I was typing that. ๐Ÿ™‚

spasov20:03:04

I only now noticed it on the bottom of the page paragraph that it specifies the modifiers.

jumpnbrownweasel20:03:55

:-) the examples may help also

spasov20:03:30

Yup, they're very helpful, actually. More helpful most of the time (for me at least).

spasov20:03:20

I have them showing up in Cursive's docs as well and it's a very pleasant experience so far.

Lucas Vassalli21:03:57

Is it a bad idea to dive into re-frame if I never did a webapp or cljs before?

chrisulloa21:03:49

no, i think itโ€™s a great way to learn react

chrisulloa21:03:17

based on trying out react + other state management solutions that are much more bloated, re-frame is a lot easier to learn imo

๐Ÿ‘ 4
spasov22:03:48

I've been thinking the same thing. IMO it would be wise to have some idea as to what React is doing and what problems it helps alleviate.

spasov22:03:27

But, you might have a harder time getting started. I view it the same as starting with React + Redux from the beginning. You don't really need Redux for the simpler stuff. It helps with being functional and pure and all that jazz, but it doesn't really help you understand React itself. In fact, I think it will be much more difficult.

Exoconnor22:03:54

Hi! I'm Connor. New to Clojure, really liking it, sometimes get caught in doc circles so time to start being more sociable. Is there an easy way to consistently get 'contracts' of anonymous function arguments? E.G. if I call doc on 'map-indexed' I get

clojure.core/map-indexed
  ([f] [f coll])
  Returns a lazy sequence consisting of the result of applying f to 0
  and the first item of coll, followed by applying f to 1 and the second
  item in coll, etc, until coll is exhausted. Thus function f should
  accept 2 arguments, index and item. Returns a stateful transducer when
  no collection is provided.
For some reason have to read twice to parse out that the anonymous fn expects ([index item]), whereas ([f] [f coll]) is immediately parsable.

noisesmith23:03:13

no, you're at the mercy of the author of the doc string sadly

Exoconnor23:03:25

Oh fun fun. Thank you!

Exoconnor23:03:15

Makes sense that would be hard especially once you get into the #() style anonymous functions but I had hopes.

spasov23:03:24

Looking at the examples usually provides better insight on how you might use the function.

noisesmith23:03:51

#() fns just expand

user=> '#(f % x)
(fn* [p1__2#] (f p1__2# x))
the real problem is that clojure has no type system keeping track of what functions expect or how they will be called beyond accepting invoke with a given arg or throwing an error

didibus01:03:42

honestly, I think this is more a convention problem. Even the type signature would still be a bit confusing. Like for reduce, I always forget if the accumulator is the first element or the second. I think Clojure could get pretty far just having a convention for documenting these HOF more clearly

noisesmith16:03:26

the way I always remember it: you can pass conj as the fn arg to reduce and it just works

noisesmith16:03:43

or str and the result is concatenated, not scrambled

Exoconnor23:03:32

...oof damn I had reduce arg order written down as [value accumulator]...that's why that function wasn't working at all. (Taking notes: doesn't work if you take the wrong notes, and I had it written with such confidence that there was a huge blind spot debugging that part. Thank you @U051SS2EU, for those particular examples ๐Ÿ™ƒ

4