Fork me on GitHub
#beginners
<
2018-12-28
>
veix.q503:12:43

how do i set the entry point for the project in deps.edn?

seancorfield03:12:57

@veix.q5 Using :main-opts or on the command-line: -m entry.point to tell it to run entry.point/-main

seancorfield04:12:19

@veix.q5 Did you ask about manifold streams? I was figuring that out for you and when I came back I couldn't find the question...

seancorfield04:12:00

The answer seems to be to call (.description s) and you'll get back a hash map describing the stream. That's not all the fields tho'...

seancorfield04:12:09

There are also .producers, .consumers, .messages, and .capacity which might return useful/interesting values for you.

seancorfield04:12:36

(took me a while of digging through the source to figure that out 🙂 @veix.q5)

veix.q505:12:06

@seancorfield appreciate it. manifold.stream/description did it for me

veix.q505:12:35

hmmm, seems like manifold.stream/downstream gets the consumers. the other methods you mentioned will sure come handy

olekss.janis09:12:44

Hi, people. Is it normal in clojure, to keep global shared state in atom, and each time a part of the structure (for me atom is vector of ongoing game states) changes (individual game state is updated) I have to swap the atom with updated positional association?

pavlos10:12:16

For lack of a better answer, I'm taking the chance to chime in. In my understanding, atoms can be useful indeed when you have a shared state like in your use case. I have seen it used like you described so judging from my really short experience it's valid, though rare and should be avoided when possible.

valtteri11:12:40

@ it’s more common to use a map instead of vector because in many situations maps provide better developer ergonomics. For instance:

user> (def a (atom {}))
#'user/a
user> (swap! a assoc-in [:game1 :players] ["p1" "p2"])
{:game1 {:players ["p1" "p2"]}}

olekss.janis14:12:31

hmm, I am using vector exactly coz it's also Associative structure, and one can do same with indexes... but I guess there is merit in your approach, as then each game identity can be it's "key"

olekss.janis14:12:41

@pavlos I came to atoms, coz essentially what I need is to have server (using ring) which runs the game, but game can have 2-4 players, which have to participate, so I can't use :session, so I have to use some structure on the server which is "visible" for all 2-4 players, so I was led to atoms, and currently they look ok, given that my game-server currently will be only in-memory Trying to implement board game - Cave Troll

pavlos15:12:21

Ah, I see. Any reason you need it to be in memory? I think I'd use persistence rather than an atom to capture the shared state!

olekss.janis19:12:45

@pavlos reason - me being beginner, so wanted first to operate with simple functions in memory, later will dump them into some file/db whatever, and it's easier to do REPLing when all stuff is in memory, and there is minimal side-effective functions involved

olekss.janis19:12:07

I am not pretending to implement commercial game, I just picked non-trivial task to learn clojure, at the same time - being deterministic and clear rules with limited "actions", so I can comprehand and pack them into simple clojure program

olekss.janis19:12:21

even writing simple function like - calculating which player's turn is next, is big learning for me. It is not easy to unlearn imperative thinking and do functional thinking, given I have build my career on imperative Java and still, being payed to do imperative code

pavlos15:12:40

Yeah I get it, one step at a time 😉 Good luck!

pavlos09:12:54

Hey all! Currently writing some unit tests for a function and I have a question

(defn inner-fun [arg]
  (throw (ex-info "msg"
                  {:arg arg})))

(defn a-function [arg]
  (map inner-fun arg))
The following doesn't work (is (thrown? Exception (a-function "test")))
FAIL in () (form-init234056825029385.clj:1)
expected: (thrown? Exception (a-function "test"))
  actual: nil
I have to write it like this (is (thrown? Exception (doall (a-function "test"))) Is this the recommended way to do it or is there a better one, possibly without doall?

rahul08032710:12:06

@pavlos map is ideally meant to be used with functions that dont do side effects like throwing exceptions as it expects a return value. Also its lazy, hence the doall. If your mapping functions has side effects I'd rather use (run! inner-fun arg) which returns nil. That way you should get the exception

pavlos10:12:07

Ah, that makes sense, thanks a lot! What if inner-fun typically returns a value but throws an exception in some edge cases? In that case what's the recommended way to propagate an exception to a-function as well?

pavlos10:12:22

i.e. I want a-function to fail if inner-fun fails at least once

rahul08032710:12:43

in that case I'd return some invalid value and check it afterwards

rahul08032710:12:17

Its generally good practice to not mix up code and side effects.

pavlos10:12:38

Oh, I see. I'm used to the "throw early, catch late" adage, so this catches me a little bit off-guard to be honest 🙂

rahul08032710:12:37

yeah in functional programming its just easier to reason about code if you keep these things separate.

rahul08032710:12:37

in clojure we generally use the convention of having a ! at the end of function names which may do side effects

rahul08032710:12:45

its okay to throw an exception here just the usage of map is non trivial.

pavlos10:12:22

Yeah it is (non trivial). To be honest I'm not yet 100% sold on the best way to do exception handling in clojure (custom error codes kinda seems like a regression but mixing code with side-effects is inherently bad, like you said). However, you've been really helpful. Thanks for your time! 🙂

rahul08032710:12:23

Happy to help. Throwing exceptions is totally idiomatic in clojure though. Thats how most functions are written. How to call them is what matters most i think.

nikola.kasev12:12:30

If I try to update a map with a key that doesn't exist, I get a NullPointerException, for example (update {1 100, 2 300} 3 inc). Is there a smart way to insert in the map a k/v that don't exist and apply a function otherwise?

nikola.kasev12:12:59

This is what I come up with:

(defn player-scores
  [player
   points
   players-map]
  (if (nil? (get players-map player))
    (conj players-map [player points])
    (update players-map player #(+ % points))))

delaguardo12:12:51

(update {1 100, 2 300} 3 #((if % inc identity) %))

rakyi12:12:57

look at fnil

carr0t12:12:41

boot.user=> (update {} :a #(if % (inc %) 1))
{:a 1}
boot.user=> (update {:a 2} :a #(if % (inc %) 1))
{:a 3}

carr0t12:12:07

Oh interesting, I've not seen fnil before

carr0t12:12:02

boot.user=> (update {} :a (fnil inc 0))
{:a 1}
boot.user=> (update {:a 2} :a (fnil inc 0))
{:a 3}

carr0t12:12:20

@nikola.kasev I'm confused, in the initial example you gave, why you're conjing when the key doesn't exist, instead of associng?

nikola.kasev12:12:08

@carr0t conj will add a new k/v pair, takes it from the vector with two elements

nikola.kasev12:12:39

@delaguardo, @rakyi thanks

jesse.wertheim12:12:18

fnil is one of those easy to forget functions that ends up cleaning up a lot of code when not forgotten (I say as someone who has occasionally forgotten about it) 😉

carr0t14:12:46

@nikola.kasev Yeah, I know conj will do that. I mean the normal use for conj is for regular lists, not maps. When I see conj, I expect a vector, list or similar. (conj players-map [player points]) could also be expressed as (assoc players-map player points), which makes it more explicit that you're dealing with a map, follows the pattern in your else case more closely etc etc. I don't understand why you'd ever use conj on a map like that instead of using assoc

kaffein14:12:49

Hi people!! has anyone ever played with cognitect aws-api please ? I have a little issue while exploring the lib through the examples ... When I try to get a var via the aws/client function, the repl just hangs

ghadi15:12:42

@kaffein does the var s3 get bound? Can you type s3 into the REPL?

kaffein15:12:17

I can't, it's like if the repl had blocked actually

kaffein15:12:00

well actually it returned (I had it running in my other terminal so I couldn’t see it)

kaffein15:12:15

and I think I may have found the problem : I @ghadi... the supposedly optional value of one of the params to aws/client is not correct

kaffein15:12:40

There it is ...

kaffein15:12:54

thanks for your help @ghadi

ghadi15:12:48

Ah, yes. You don’t have to provide the region if it’s in your ~/.aws/config @kaffein

ghadi15:12:05

Ghadis-MacBook-Pro:~ ghadi$ cat ~/.aws/config
[default]
region = us-east-1

ghadi15:12:21

or if you’ve set AWS_PROFILE and that profile within .aws/config has a region set

kaffein15:12:51

according to the documentation, there is a default value set by the lib actually ... so that’s weird

kaffein15:12:52

anyway, I will set it in the ~/.aws/configas you suggested

ghadi15:12:03

yes the provider itself is defaulted, but the default provider can’t guess information that isn’t there

kaffein15:12:52

thanks @ghadi 😉

nikola.kasev16:12:47

I'm doing day 9 of Advent of Code, what would be an efficient data-structure for a ring of integer values where you have to take elements out, put elements in on an index, and also go clockwise and counter-clockwise?

kinnes16:12:19

https://en.wikipedia.org/wiki/Doubly_linked_list might be viable? Not sure how efficient it is but I suppose that would depend on the implementation.

kari.marttila16:12:51

I'm using a Java library accessing Azure Table Storage (couldn't find native Clojure library). I made good progress with Clojure/Java interop but I have one difficulty implementing a class in Clojure that I need to supply to the library. I need to create the following class in Clojure public class CustomerEntity extends TableServiceEntity { public CustomerEntity(String lastName, String firstName) { this.partitionKey = lastName; this.rowKey = firstName; } public CustomerEntity() { } public String Email; public String getEmail() { return this.Email; } public void setEmail(String email) { this.Email = email; } } ... so that I'm able in Clojure to make a call like in the Java example: TableQuery.from(CustomerEntity.class).where(partitionFilter); I'm a bit puzzled should I implement the CustomerEntity class using proxy, gen-class or what?

kari.marttila16:12:54

... a nice exercise to learn how to use Clojure/Java interop in real life if I can make it work. 🙂

jumar17:12:14

I think you need gen-class since you have to pass named class to the java code. It might be better to actually do this in plain Java instead of clojure.

kari.marttila17:12:52

Thanks for quick reply! I'll first try to create a gen-class, this is my personal exercise to learn Clojure features, so no hurry here. If I'm not able to make progress I downgrade this part to Java. 🙂

kari.marttila17:12:53

I followed the diagram and it says: "use gen-class" 🙂

kari.marttila17:12:39

I wonder if I could find a very simple gen-class example which provides a couple of constructors and a getter/setter...

kari.marttila17:12:43

Unbelievable. I think this is going to work. I made a very simple gen-class that just extends the base class the library requests. Then I made the query using that gen-class and it returns 2 items just as I know it should: (count (into [] (. productgroup-table execute table-query))) => 2 Now I just have to figure out how to provide the member variables and their getters/setters for that class and how to get the values from the items.

kari.marttila18:12:41

Using Clojure REPL experimenting with a new Java library seems to be easier than using Java to experiment with a new Java library.

kari.marttila18:12:42

Unbelievable. I did it. I used a Java decompiler to see what kind of Java class actually got created. The base class provided getters for partitionkey and rowkey. Now it is simple to call the getter: (. (first (. productgroup-table execute table-query)) getRowKey) => "Books"

kari.marttila18:12:09

This is like magic. I have been programming Java for some 20 years. I got used to all these edit-compile-build-test cycles. Now using Clojure REPL I can dynamically call the Java library methods and experiment in a way that was never possible with Java.

kari.marttila18:12:11

I thought this is going to take at least a couple of days to learn enough Clojure/Java interop so that I'm able to make the first queries - It took a couple of hours. Clojure is so beautiful and the Clojure REPL just rocks.

minhnhat10bk18:12:30

i write my java class

minhnhat10bk18:12:45

how to use it in clojure

smnplk18:12:01

@minhnhat10bk First you import it in your namespace declaration.

smnplk18:12:05

(ns my.namespace (:import (my.package.MyClass))

smnplk18:12:00

if you import only one class (:import my.package.MyClass)

smnplk18:12:04

Then you can use the interop 'sugar' to instantiate objects, call methods, call static methods ..etc. All explained here https://clojure.org/reference/java_interop

minhnhat10bk18:12:18

my class path is src/pkg/myclass

minhnhat10bk18:12:25

(import '[pkg myClass])

minhnhat10bk18:12:41

but I get Syntax error (ClassNotFoundException)

seancorfield18:12:55

@minhnhat10bk What is the package statement in your Java code?

seancorfield18:12:38

(also, did you compile it? Clojure needs the .class file, not just the .java file)

minhnhat10bk19:12:39

after build it to .class

minhnhat10bk19:12:47

It ok right now

seancorfield19:12:52

@minhnhat10bk If you're using Leiningen, you can have it compiled automatically when you run Leiningen. See https://github.com/technomancy/leiningen/blob/master/doc/MIXED_PROJECTS.md

mss23:12:53

very beginner lisp macro syntax q: is there a way to return multiple forms out of a block? so something like:

(assoc {} :a 1
          (if condition
            '(:b 2)))

mss23:12:41

the idea would be that if condition was met, the two forms would be applied. I know there are other syntactical ways to achieve something like this (`apply`, cond->, etc). just wondering if there’s a more concise syntax