Fork me on GitHub
#beginners
<
2019-09-14
>
mattpend00:09:51

Is there a more idiomatic way of accomplishing this?

(def s '(({:a 1}  {:b 2}) ({:c 3} {:d 4})))
(map (fn [data] (reduce merge data)) s)
I want to transform (({:a 1} {:b 2}) ({:c 3} {:d 4}))) into ({:a 1 :b 2} {:c 3 :d 4})

nmkip01:09:03

(map (fn [[m1 m2]] (merge m1 m2)) s) maybe this? It might not be what you are looking for because it only works if each of the sub-sequences contains exactly two maps. If that's the case, then this seems more readable to me.

mattpend02:09:25

It does in this case! I like this more then my initial solution in which I destructured with first and second. I itch a bit about the readability reduce to others but I suppose I should assume it's not an issue.

😁 4
ryan02:09:24

not sure if its more idiomatic but perhaps (map #(apply merge %) s)

ryan02:09:47

reduce feels more clojuric /shrug

chrisulloa00:09:14

It seems like a good solution, pretty readable in my opinion

👍 4
David Omar00:09:23

I have some troubles trying to keep tht non-mutability in mind. I'm writing a program that will generate subway networks. Each station has a next and prev keys that indicate the next and previous stations. When creating a line, I send the two stations that are the start and end of the line. (So, at first, every line only has two stations) But I should also update the next and prev values for start and end. But I haven't find a way to do this. How would you proceed?

andy.fingerhut01:09:58

If you have become accustomed to writing programs in mutable languages, this can be something that requires some practice and experience to learn new ways of managing such data structures.

andy.fingerhut01:09:47

I would ask you to consider not having next and prev keys at all, if you do not absolutely need them -- and they might be needed for fewer reasons than you initially would think.

andy.fingerhut01:09:52

I do not know if it would be more helpful or confusing to consider this, but there is an existing library called ubergraph that I have found useful for manipulating graphs, where "graph" here means the mathematical notion of a graph, with a set of nodes, and a set of edges that relate pairs of nodes.

andy.fingerhut01:09:42

ubergraph implements these graphs as immutable objects. If you want to add a node, or an edge, it is like adding an element to an immutable set -- the old set/graph/whatever still exists and can be accessed if you want it (or you can forget it if you don't need it ever again), and a new set/graph/whatever is returned from the 'update' functions.

andy.fingerhut01:09:41

If you want to take advantage of already-implemented algorithms in the ubergraph library, such as finding shortest paths, connected components, etc., I would consider giving it a look: https://github.com/engelberg/ubergraph

andy.fingerhut01:09:55

If you don't need such things, then it might be more trouble than it is worth to learn right now.

nmkip01:09:00

I don't need it but I already starred it

4
David Omar01:09:43

Yeah, I'm struggling real hard. I'll check ubergraph. Thanks for answering.

andy.fingerhut01:09:33

ubergraph does internally maintain a set of neighbors for each node, for you, whenever you add and remove edges. So there are efficient ways to say "tell me all neighbors for node X in graph G"

sova-soars-the-sora14:09:44

@davidomarfch next and prev are relative terms, right? like the "absolute" subway line will not have any nexts and previous/ it'll just have a completed sequence of stations right? Also, do the start and end have to be touching in your algorithm (and grow from initial position), or does the algorithm "find" the route?

sova-soars-the-sora14:09:19

work backwards from the result you want

David Omar15:09:46

The algorithm finds the route. It's recursive. Starts with two points, and inserts one point near the midpoint between the two, and repeats. I think that idea of recursiveness could be easy to implement in a functional way.

Leon16:09:54

okay folks!

Syntax error (NullPointerException) compiling at (1:1)
with no helpful information in the stack-trace (all pieces are either working when tested directly, or are internal stuff,...) any ideas where i can start debugging? these error messages really drive me crazy..

Leon16:09:09

and on a tangent. how can a NullPointerException be a Syntax Error?

paul a16:09:49

when i've encountered that error recently, i think i've found the cause to be misspelling some identifier

naxels18:09:16

Hi guys, i would like to learn about real world development with Clojure as i'm trying to design/learn how to be a build an API web server while working in the REPL

naxels18:09:37

Now im doing the change file, restart with lein run loop

mjw21:09:18

@UE4AGNU75 If you’re not quite ready to commit to using something like Component or Mount, I use the following in my Component-less projects that allow me to call (start-dev), (stop-dev), and (restart) to recreate the server without needing to completely restart the repl:

(defonce server (atom nil))

(defroutes routes
  ...)

(defn start []
  (jetty/run-jetty routes {:port 3000}))

(defn start-dev [& args]
  (reset! server
          (jetty/run-jetty routes {:port 3000 :join? false})))

(defn stop-dev []
  (db/stop-dev)
  (when @server
    (.stop @server)))

(defn restart []
  (stop-dev)
  (start-dev))

(defn -main
  "Start the application"
  [& args]
  (start))

mjw21:09:44

The idea is that storing state (like your running server) in an atom allows you to start from scratch with a single function call instead of restarting the JVM.

mjw21:09:31

I’m not familiar with Emacs (I’ve used Vim for years), but Emacs should provide a way to refresh the running REPL with the current version of the files you change.

mjw21:09:03

So my workflow is: 1) Change a file 2) Save and reload the current namespace (using your editor’s integration with the Clojure REPL) 3) If the change I made impacts the current state at all, then (restart)

mjw21:09:31

Without a tool like Component or Mount, you would need to create start and stop functions for your different types of runtime state and then tie them together in your top-level (start-dev) and (stop-dev) functionality.

naxels21:09:48

Thanks mjw!

naxels21:09:03

It's not that i don't want to use Component

naxels21:09:17

It's feels a little beyond my reach as a beginner right now

naxels21:09:53

I will try your solution :)

jumar05:09:56

If you have an editor with REPL integration you can just change a function and re-eval it. No need to restart anything. Unless you change routes, but even that should be possible when you pass it like #'routes when starting server

mjw18:09:21

Right. That step 3 above is only necessary if the resulting state is no longer valid/couldn’t possibly been produced by the current state of the codebase.

naxels18:09:55

While testing functions in Emacs/Cider (also a beginner at this)

naxels18:09:01

Does anyone know of some video's of people doing real coding like this on video in the repl?

Jakob Durstberger18:09:37

What a coincidence. I watched a talk of the creator of Luminus yesterday and I think it is really good. The title is a bit misleading https://www.youtube.com/watch?v=nItR5rwP4mY

johnjelinek18:09:15

there's a thread going around on people's workflows methinks, I'll see if I can find it

naxels18:09:27

Awesome, thanks :)

johnjelinek18:09:42

but if you're doing lein run after every change ... it sounds painful

naxels18:09:42

I heard about Components and/or ring-devel

naxels18:09:05

But especially Components looks more difficult

naxels18:09:56

And i already have a main loop that starts HttpServer

seancorfield21:09:11

@UE4AGNU75 If you start your HTTP server in your REPL, you can continue working, editing code and evaluating it into the app while it is running in your REPL.

seancorfield21:09:42

Component helps a lot with that because it has the idea of start/stop lifecycle for things (web servers, database connection pools, etc).

seancorfield21:09:45

This is an example you can look at for a small web app that uses Component, Compojure (for routes), Ring, Selmer (for HTML templates), and next.jdbc (for the database). https://github.com/seancorfield/usermanager-example

seancorfield21:09:02

In particular, it shows how you can start and stop the web server inside the REPL: https://github.com/seancorfield/usermanager-example/blob/master/src/usermanager/main.clj#L189-L196 /cc @UM8P1G72Q

seancorfield21:09:58

My workflow is to start up a REPL (in my case with a Socket REPL and Cognitect's REBL both running as well) and then connect my editor to that and start the application inside the REPL and leave it running while I develop and test code in the editor, evaluated into the REPL. I often have my REPL running for days, sometimes even weeks without a restart.

naxels08:09:22

Thank you Sean!

naxels08:09:30

I will look into this

naxels08:09:44

I too would like to get to that level

naxels08:09:36

Where i can leave in my case Spacemacs + Cider running for days with nRepl and never have to worry about it, while enjoying the development benefits

naxels18:09:05

So i dont know about ring-devel either

johnjelinek18:09:29

personally, I don't use lein in any of my stuff these days

naxels18:09:47

Except for lein deps? Haha

johnjelinek18:09:52

I don't do that either

johnjelinek18:09:06

to me, all of lein is deprecated ... but that's just my preference

Jakob Durstberger18:09:37

That sounds interesting as basically every project I've found so far uses leiningen. How come you are not using it?

johnjelinek18:09:30

lein is how you did it for years, but it wasn't a feature of the language, it was a third-party tool. tools.deps now comes with the language and does all that I was using lein for, so I don't need it anymore

Jakob Durstberger18:09:46

maybe this is going to deep, but what is your testing workflow?

johnjelinek18:09:40

well, in Clojure, I don't usually do TDD like I would in other languages, there are more effective ways to get fast feedback in this paradigm

johnjelinek18:09:28

so, I do repl-driven development and if I need to do a test for confidence that something doesn't break, I write a test for that purpose

johnjelinek18:09:57

but, in a lot of cases, I can have my tests just run some functions against my specs

Jakob Durstberger18:09:49

hmmm ... interesting. Do you have any resources to this style of development?

johnjelinek18:09:35

it's been trial and error for me personally, but I hear really good thing about the videos by @U050P0ACR

Jakob Durstberger18:09:32

Thank you, this course seems to have a lot in it but I don't think I can afford it 😞

johnjelinek18:09:53

(that's why I haven't tried it yet)

johnjelinek18:09:07

I am thinking about the monthly subscription though

Jakob Durstberger18:09:10

I guess the investment of $50 a month would make me use it as much as possible

Jakob Durstberger18:09:21

anyways, thank you for taking time in answering my questions

andy.fingerhut20:09:33

If you search for the terms "sean corfield clojure" on YouTube, you can find 3 videos of his number 1, 2, 3 where he demonstrates his development workflow - he uses deps.edn and REBL

naxels18:09:55

I'd like to learn how i can get to that level haha

Mattias19:09:31

Hey, I’m just trying out the Java interop stuff. Anyone explain why this doesn’t work like I naively thought?

Mattias19:09:00

(doto (new java.lang.String "fno") (.concat "abc") (.concat "fni"))

Mattias19:09:08

Guessing some magic with strings being interned... but I don’t like surprises 😅

andy.fingerhut20:09:14

Java strings are immutable. What behavior did you expect to happen?

andy.fingerhut20:09:19

doto was created more for use cases where there is a mutable Java object, and you wish to do a daisy chain of multiple method calls, each of which mutates the object, and returns a reference to it.

Mattias20:09:55

Doh. Ok. I assumed a new object was returned. Thanks for sorting that out!

andy.fingerhut20:09:52

The new call you have does return a new object, and it is passed to the concat method, but the concat method returns a new object that your doto form ignores and is thus discarded.

andy.fingerhut20:09:05

With that in mind, take a look at the output of (doc doto) and see if its behavior makes sense now.

borkdude21:09:00

The -> macro way work for Matt's use case

👍 8