Fork me on GitHub
#beginners
<
2018-12-22
>
peter-kehl07:12:03

Hello lovers of metadata. (meta ^:extra even?) returns nil. Metadata reader macro ^ is not supposed to work on symbols, as per https://clojure.org/reference/reader#_metadata. On the other hand, it throws a syntax error for non-IMetas (such as ^:extra 1). Hence, when it doesn't throw, one could expect it to work. But why doesn't it then throw a syntax error, instead of being silent?

seancorfield07:12:38

user=> ^:extra even?
#object[clojure.core$even_QMARK_ 0x41d7b27f "[email protected]"]
user=> (meta (with-meta even? {:extra true}))
{:extra true}
user=> (meta ^:extra even?)
nil

peter-kehl07:12:57

Yes. But since both following ways apply the metadata to {} literal

(meta (with-meta {} {:extra true})) ;=> {:extra true}
(meta ^:extra {}) ;=> {:extra true}
wouldn't you expect the same when applying to a symbol?
(meta (with-meta even? {:extra true})) ;=>{:extra true}
(meta ^:extra even?) ;=> nil!

peter-kehl07:12:19

I don't mind that ^:extra even? doesn't apply meta (as per documentation it's not supposed to), but it would be helpful it if reported an error.

seancorfield07:12:13

There are a lot of things in Clojure that have undefined behavior -- and coming from a C/C++ background I think that's fine.

seancorfield07:12:49

When something isn't specified, the most performant option is a reasonable choice.

seancorfield07:12:07

When the doc/spec says something "doesn't apply", that doesn't mean it should produce an error or that it should behave in a particular way.

seancorfield07:12:26

I only care if the docs say "X should work" and it doesn't 🙂

peter-kehl07:12:37

Thank you. I can see this perspective gives more peace of mind.

seancorfield07:12:35

clojure.set is a great example of this -- lots of its functions behave very weirdly if they are not given sets as arguments, but their docstrings only define their behavior for set inputs.

1
andy.fingerhut07:12:10

The Eastwood linter gives warnings for some things that Clojure does not give an error about, that might be mistakes in your code, but not necessarily everything you might wish it would warn about: https://github.com/jonase/eastwood

👍 1
Ali Ebadian11:12:59

Hi all, I have simple function that adds the first two numbers in a vector : `(defn adding-first-two-of-v [v] (if (< 1 (count v)) (+ (nth v 0) (nth v 1)) (nth v 0) ) ) ` what would be the correct way of adding looping over a given vector?

Josip Gracin14:12:05

hi @ali.ebadian, your function can be implemented in a more simple way like this: (defn add-first-two [v] (apply + (take 2 v)))

Ali Ebadian15:12:29

@josip.gracin thanks, looks like i got a long way to go

drewverlee17:12:39

In general, how do ppl go about spelling checking there clojure programs?

drewverlee17:12:57

strings, symbols, keywords, etc..

andy.fingerhut17:12:54

Do you mean, looking for errors that will cause the program to misbehave? Or do you mean, are these names in this English/Dutch/French/etc. dictionary?

drewverlee17:12:53

Both? This is probably a editor specific thing. On spacemacs with the lintet joker, the mis spelled symbols aren't highlighted. I can use ispell buffer to check for dict words, but then it seems like I would need to add a lot (fn, nil, etc...)

drewverlee17:12:49

I think something that accepted none project code as valid, but raised warnings about code I added wild be good enough

drewverlee17:12:07

I suppose I could run a spell checker on the code diff

Chase18:12:56

i'm curious about deployment of clojure programs once I (if ever) finish a real project. So something like lein uberjar creates a .jar file right? And then what? I give that .jar file to a user and they can just run the program? I tried to move the snapshot.jar file out of the project and practice just randomly opening it and couldn't figure that out.

Lucas Barbosa18:12:51

lein uberjar produces two .jar files, one with and one without the runtime dependencies of your project. Both are executed via java -jar, but you must provide the proper classpath to the latter.

bronsa18:12:32

@peter.kehl @seancorfield re the metadata questions above -- what you're observing is a bit more nuanced than that, the disconnect you see wrt with-meta ^ and collections/functions is due to when that metadata is applied

bronsa18:12:04

when you do ^:foo even? or ^:foo [1], {:foo true} is attached at read time to the object and available at compile time

bronsa18:12:20

but for clojure collections and symbols, compile-time metadata is propagated at runtime

bronsa18:12:43

which is why ^:foo [1] behaves like (with-meta [1] {:foo 1}) but not or ^:foo even?

bronsa18:12:31

also, reader metadata is supported on symbols, but the syntax would be (quote ^:foo x) so '^:foo x not ^:foo 'x which would be ^:foo (quote x), thus applying the metadata on the quote expression rather than on the quoted symbol

JanisOlex18:12:37

hey I have a question... Currently I use {:input 'my-input-function} and then I use ((resolove (:input myfunctionmap)) to invoke that function - but there should be more idiomatic and more beautiful way of doing so What I want is to be able to change some keys referencing input and output functions for my program, and use them when need either input or output

Lucas Barbosa18:12:37

Is there a specific reason why you are putting the symbol in the map, instead of the function itself? Otherwise, putting the function instead of the symbol would remove the need for invoking resolve. Also, you can take a look on dynamic vars and binding, which would remove the necessity for the map. Since I am also a beginner, I am interested in seeing some other technique for doing this.

JanisOlex19:12:00

well I have defined like default input and output functions somewhere... which are stupid read-line and println respectively

JanisOlex19:12:18

but I want my application eventually to work with web input and render to html as output

JanisOlex19:12:16

so I want all the other program be totally unaware where it is geting it's input from and how output is being performed... so I was thinking to put those references of functions into map which is in general whole data of whole program, and when passing around, it is used by program to read and write stuff

Lucas Barbosa19:12:42

I see. I will come up with a quick binding example, I believe it will suit your needs

JanisOlex19:12:43

with my guts I feel I am not doing it idomatically, but how else one can reference functions?

Lucas Barbosa19:12:23

(def ^:dynamic *my-input* 3)
(defn print-my-input
  []
  (println *my-input*))
(print-my-input)
(binding [*my-input* 200]
  (print-my-input))

JanisOlex19:12:14

well I don't think this is the case

Lucas Barbosa19:12:32

Instead of the number, you can use a function

JanisOlex19:12:42

what I want is to have data structure, which also contains (by keys) function which can do something, like input and output itself

JanisOlex19:12:07

and then program which uses the data structure, just looks up the input/output function by key inside the data strucutre, and uses it to get inptuts and outputs

JanisOlex19:12:16

I need dynamically reassignable function, not value

Lucas Barbosa19:12:36

You can define the dynamic var as a function. The number was just an example

JanisOlex19:12:34

but then it looks like the var lives separated from my data structure

JanisOlex19:12:10

probably it's my OOP kicking in, but I want to have function of the data strucuture to live inside that data structure - probably bad idea

JanisOlex19:12:36

but it simplifies my program, so that all of my top level functions all take only one parameter - my data structure

JanisOlex19:12:13

and then separate functions just pick parts of the data strucutre, But occasionally input is needed and output is needed, so it would be nice to reference the same data structure for the "currently configured" input/output functions

JanisOlex19:12:51

of course, when the data structure is initialized at the beginning of the program, those input/output functions are defined assigned...

JanisOlex19:12:42

my approach works, but is it good and correct one?

Lucas Barbosa19:12:17

If you want to keep the function in the map, you can put the function itself as the value of the association. Your example was: {:input 'my-input-function} and ((resolve (:input myfunctionmap)) You can remove the quote and the resolve: {:input my-input-function} and ((:input map))

Lucas Barbosa19:12:53

If you want to change the function in the map, just use (assoc map :input new-func)

JanisOlex19:12:23

just found it

JanisOlex19:12:46

so I just made expedriment and it works, looks like this is what I want (def myoutput println) (def struct {:output myoutput}) (let [output-function (:output struct)] (output-function "Whatever"))

JanisOlex19:12:35

yea you are right that ' symbol and resolve are excess

JanisOlex19:12:52

by removing both of them I got nice and clean code - thanks

JanisOlex19:12:35

now I found another problem (in future) With normal console, I know when I need input, and I explicitly ask, and block program waiting for tha tinput But for web... it's not that possible... for that I have to somehow wait for input differently, as user will be invoking some REST API as "input" to my program, and where in the first case app is initiator of input, in other case it is remote user kicking my server through API giving that input at some arbitrary time

Lucas Barbosa19:12:18

That is because the API is more "reactive" than "imperative". Your program will react to requests. You can build the console version of the program in the same way as the web version is. The thing both have in common is the way the input is dealt with, so you can separate this logic in a pure function. The difference is the way the input is taken, so you will have to separate the read/write logic. For the API case, you will get the request, pass it to the logic function, grab the return and write it back to the client. For the console case, you will prompt the user for input, pass the input to the function, grab the result and output to console.

Lucas Barbosa19:12:38

Things will be easier if you separate the logic from the input/output handling

Lucas Barbosa19:12:59

Instead of putting the in/out funcs in the map, you can completely remove them and pass only the data to the function. The function will return the result and then you deal with giving it back to the client. Each interface (API/console) will implement it's own ways of handling IO

JanisOlex19:12:08

yea, I guess I will do this, coz now I see that input/output got inside function which should not do anything of input/output just react on it... and don't care where it came from

JanisOlex19:12:21

that "input/pure/output" sandwitch is what I want to achieve

JanisOlex19:12:41

ok, thanks... will think about it

Lucas Barbosa19:12:06

this is why people will often talk about building functional systems as an onion. The core will have only pure functions, and the side-effecting functions will be pushed toward the outside

Lucas Barbosa19:12:46

it is way easier to unit test systems like this

JanisOlex19:12:35

yes sure... and that is essential for me too, as I have not much knowledge at all about clojurescript and ring and compojure etc. so until I will get to web, i want to test my program without the web at all... pure console input/output and then I will start learning next things - clojure for web

JanisOlex19:12:15

but as my program in general is "game" (board game) I have to read turns from users, where each user can have several actions on each of their turn So it is somewhat thinking need to be done Like infinite loop of inputs/actions/outputs -> inputs/actions/outputs -> and so until end is reached

Lucas Barbosa19:12:39

That is normal, I am also still learning. It was very hard, especially in the beginning, to wrap my head around the functional style, coming from an objected oriented perspective. It is just a matter of practicing a lot, reading other people's code (libraries, applications, etc.)

JanisOlex19:12:06

and whole game state is kept in one huge map, but then I have to "chop" each step into some "snapshot" of the game, so I can use reactive approach, and always know on what stage where I am

Lucas Barbosa19:12:29

I see. You may want to take a look on Clojure's reference types, to maintain the state of the game as time progresses. That would be useful for both web and console version

Lucas Barbosa19:12:10

Especially in the web version... that would allow you to maintain the the state of the game in memory, and update it as requests come from the web

Lucas Barbosa19:12:24

(in a thread safe manner)

Lucas Barbosa19:12:57

You can define the valid transformations to the game state as functions (as you may already be doing). When a request comes, you decide which operation is to be done, apply it to the current state of the game and get the resulting state as a return from the function. From there, you can write to the reference type and return something to the client

JanisOlex19:12:34

yes, this second part I figured, now I need to remake some stuff, to make the input part correct, and yes, transformations are the one good approach

Lucas Barbosa19:12:41

cool... make sure to come back to the channel and ask if you have any modeling questions. There are pretty smart people in here that can help you

peter-kehl19:12:19

@bronsa Thank you. I see now: (meta (quote ^:flag even?)) ;=> {:flag true} Does it mean that when using ^ with a symbol, it attaches the meta to the symbol before the symbol gets evaluated? If so, does it mean that literals get evaluated before symbols? But wouldn't you naturally expect it to attach the meta after the symbol gets evaluated?

bronsa19:12:58

^ always attaches the metadata to any form before it gets evaluated

bronsa19:12:07

symbols are no exception or special case

bronsa19:12:27

remember that ^ is a read-time construct

bronsa19:12:44

user=> (meta (read-string "^:foo a"))
{:foo true}

bronsa19:12:12

user=> (meta (read-string "^{:foo (println \"x\")} [1]"))
{:foo (println "x")}

bronsa19:12:29

user=> (meta (eval (read-string "^{:foo (println \"x\")} [1]")))
x
{:foo nil}

bronsa19:12:33

which follows from what I described above: for clojure objects (collections, symbols), the metadata is propagated from read-time to runtime (and thus gets also evaluated)

bronsa19:12:43

for symbols it can appear a bit more confusing since you can't produce a runtime symbol to which you can attach metadata using the reader syntax without quoting it, so even if technically possible it's practically just restricted to collections

peter-kehl19:12:49

@bronsa Wow. Everytime I get more comfortable with Clojure, there's more to study. Simple language that allows ideas to be big. Thank you.

Denis G19:12:28

Having problems with Java interop. Does anybody know why this method is not resolved? Trying to compute successor of the TreeMap-node (java.util.TreeMap.Entry/successor ...)

Denis G20:12:22

but I see your point. it’s not public

Denis G20:12:58

it’s sad that clojure sorted-map aka PersistentTreeMap has no successor func. Feels like I have to do some mambo-jambo to make it work, or write in Java 😢

Denis G20:12:15

actually ceilingKey would work just find in my case, since this is what I want, but I want this to be done on PersistentTreeMap - sorry for kinda wrong question though

Denis G20:12:21

is it possible/can I find a successor key/entry in a sorted-map in clojure?

peter-kehl21:12:33

@denisgrebennicov Did you try clojure.core/subseq? Its doc: ([sc test key] [sc start-test start-key end-test end-key]) sc must be a sorted collection, test(s) one of <, <=, > or >=. Returns a seq of those entries with keys ek for which (test (.. sc comparator (compare ek key)) 0) is true.

👍 1
Denis G21:12:41

i’m interested in O(logN) complexity. Are there any guarantees while using subseq?

Denis G21:12:27

according to https://github.com/clojure/clojure/blob/clojure-1.9.0/src/clj/clojure/core.clj#L5049 it just applies seq and takes the items while the test is true

Denis G21:12:47

ooo but seqFrom appears to be more smart on the collection, interesting

peter-kehl21:12:55

You can also reverse a sorted collection efficiently with rseq.

doteka21:12:11

I'm banging my head against the wall here.. What's the correct way to clear a session with luminus/immutant? Right now I'm doing this:

doteka21:12:27

(defn logout []
  (-> (response/found "/")
      (dissoc :session)))

doteka21:12:13

But on the next request, the session appears unchanged...

doteka21:12:19

Eh, figured it out - instead of removing the session from the response map, I had to set it to nil

Daouda22:12:32

Hey guys I am reading clojure reference doc about The Reader and the first line start saying Clojure is a homoiconic language, which is a fancy term describing the fact that Clojure programs are represented by Clojure data structures.

Daouda22:12:11

Can someone explain me better what does that frase really means?

Daouda22:12:51

Show me that with example please

Daouda22:12:06

Can be explanation also

seancorfield22:12:29

@quieterkali It's core to why macros work the way they do. Consider (defn foo [a] (println "Hi! a))... that's a list with four elements...

seancorfield22:12:50

(list 'defn 'foo '[a] '(println "Hi!" 'a))

seancorfield22:12:02

and the third element is a vector with one element

seancorfield22:12:47

(list 'defn 'foo (vector 'a) (list 'println "Hi!" 'a)) -- that would compute, at run time, the data structure that is also a function definition

seancorfield22:12:50

Does that help?

✔️ 3
Daouda22:12:54

That helped a lot:pray:

Daouda22:12:19

Really helped, thank you