Fork me on GitHub

Looking for feedback on a set of slides I put together (I'm a clojure newbie myself).


NIce! But I am a Common Lisp developer trying to think like a Java developer, so “Preserving T1 to whoever still has a reference to it.” sounds like a bug. There is one T but my app now has two inconsistent copies of T. Does the code looking at T1 know it is a legacy T? How do we manage this semantic reality in a world of immutable state?


Another thought: the “Hello Java/Clojure” slide misses the point of K&Rs “Hello,world”, which was specifically “what does it take to create a standalone application to print ‘hello world’ to the console”. In the case of “C” that included an #include of stdlib.h and then a link command pulling in hw.obj and stdlib. So to be fair we would need to see the project.clj and, yes, clojure happens to have println in core, but how about something simple requiring ‘clojure.string’ to show, as did K&Rs, pulling in a library? Then we need to build the standalone :)…


For me that's the biggest mental jump for a Java programmer to make. We are so used to state being manipulated that the way we do domain or problem modeling centers around it. It is hard to let go of that concept and embrace immutability. If you need state that reflects one truth, then refs and atoms are your friend, which still offer a better concurrency story than unguarded instances of Java classes


oh, and thanks for your feedback, btw!


Right, my dataflow library uses refs/atoms so there is one truth. I felt better seeing Reagent does the same. But at least one bright Clojurian reports horror at the mutability. 🙂

Caio Corrêa13:07:43

Hello Guys! How would you update a map inside a vector atom? Is there a better way other than using swap for the hole map? Here is a coding example:


so initial approach is you update the value of an atom so its a simple question of how do you update a vector of maps that you like. not sure what the use case is but for something like this perhaps a vector isn't the right data structure for you? Each thing has an id so that suggests perhaps your atom should be a hashmap from id to user record

😀 4
👍 4
Caio Corrêa13:07:07

That makes as sense! I went with vectors right away because of my JavaScript background. I thought that was a good way to store my data, but I clearly I forgotten how I was going to update them. I’m going to study a hash map implementation! Thank you!!!


Also that’s a strange name for an atom, it means if you want to use the syntactical sugar for derefto get the value you would have to type @@PROFILES which reads a little strangely.


FYI @ is short for deref hence @@PROFILES is same as (deref @PROFILES)


Typically I would name your atom like so: (defonce profiles (atom ...))


so deref’ing it would read as @profiles

David Reno13:07:30

Trying to use the plumatic/schema library to validate IP addresses. Schema doesn’t seem to have that as a pre-defined data-type so now I’m trying to follow the best process of finding and using other libraries. Is sorting through these results my best bet for finding good libraries? Is it considered bad/good to use other libraries vs writing your own? Is too much validation of type non-idiomatic in clojure? Appreciate any comments.


The guy who started golanglibs made one for clojure:


MO- good to use good, maintained libraries, otherwise write your own. The dev is responsible for the supply chain they introduce. So be careful whom one trusts.


In clj, think of validating data shape not type.


type implies both data shape and a prescribed set of operations. just worry about the former, then you have flexibility in the latter


it can be a lot to absorb and design around, but the goal of clojure.spec is to solve for the data shape problem


ah, yeah, that's very interesting, and a useful demonstration of the distinction between shape and type. That gist is really a parsing problem, and those definitions of an ip address are specific to the problem. That's fine- data shapes are often problem specific. You don't need to reuse specs. With types, however, there is a tendency towards reuse, which is often unnecessary.

David Reno15:07:13

Thanks @U0FF3A4V6 and @U83JDR147 for the feedback! I’ll go through this all and think about data shape (which is new to me) as well as the ip form example. I see that the example uses spec, should I be doing that now or learning with schema?


Maybe play with both? But spec is the future tbh


There was a time when schema was unsupported, but now it looks like it is supported, so it should be ok to use. To my eyes it is more type-oriented than data-shape-oriented. Spec is direct from Rich Hickey, represents his thinking on the matter. So- both are opinionated; spec represents the opinions of the crew behind Clojure, of the future of dynamic language program validation and testing. But, like functional programming, use of spec for dynamic language program validation is a thought train that takes practice and time to get on. Schema can feel familiar for people who only have an OO/typed programming background, and it's a good library, definitely adds value for teams that use it.


The talk recently posted from a clojure-nyc meetup about spec does a good job chronicling one journey. It may take a few attempts to figure out the right way to use it.


i am trying to make app using sente+re-frame+mount


can't make sente work


most of the code is copied from official example


but i can't send


on test app i added it and it works


i receive error like

2018-07-23 16:09:02,718 [XNIO-1 I/O-7] DEBUG io.undertow.websockets.core.request - UT025003: Decoding WebSocket Frame with opCode 1 
and after that even ping from backend stops


Hey there, can you help me understand the following behaviour:

(time (+ 1 2))
"Elapsed time: 0.026285 msecs"

(repeatedly 5 #(time (+ 1 2)))
"Elapsed time: 0.015737 msecs"
"Elapsed time: 0.001059 msecs"
"Elapsed time: 8.55E-4 msecs"
"Elapsed time: 9.35E-4 msecs"
"Elapsed time: 0.001141 msecs"
The first attempt of repeatedly returns something close to the original time of (time (+ 1 2)), but the following results don't look correct and seem like they are cached. Does repeatedly get memoized under the hood somewhere? Is there a way I can force a fresh evaluation each time of my function?


My guess is that first time is way longer than it takes Clojure merely to add two small integers, and must include some kind of one-time setup time, perhaps compiling the expression.


I can create an expression that takes about 500 milliseconds to compute, and run (repeatedly 5 #(expr)) on it, and all of the 5 times reported are reasonably close to each other, although not identical.


If you are running Clojure on the JVM, there is JIT optimization that kicks in after executing some code "enough times", but I believe that is usually at least configured to be dozens of times, not merely once. There is a library called Criterium that can help with running benchmarks, including running the code many times for a big fraction of a minute to try to force JIT compilation to be done to native code before it starts recording results.


ok, thanks @andy.fingerhut (and for your many docs examples!)


Tom Crayford’s Performance and Lies talk is a good overview of some of the challenges in benchmarking on the JVM:


Does anyone have advice or suggestions for above? Reading material is great too


hello! i'm at the early part of the Brave book that talks about anonymous functions. i was starting to grasp that it is a function without a name but it doesn't explain why i would want to use an anonymous function instead of a regular one. then it says you can associate an anonymous function with a name and my brain really didn't like that. can anyone help clarify for this noob? when and why do you use anon functions.


The “C” language might help. It had a sort function that did something weird, it let us pass in a function that would receive two items being sorted and decide which was, well, to come out first. Normally that would be the smaller, but hey. So now my problem is that I do not have anonymous functions. I have to create a named, top-level function song-is-better and then “C” was kind enough to let me take the address of that and pass it to sort. With anonymous functions, I can just on the fly pass `(fn [x y] (> (likes y)(likes x)))


is it maybe because you are only going to use it that one time?


@chase-lambert Yes, anonymous functions are useful for when you want to use something just that once. They can make your code more concise and readable by not having to define another function and place more lines into your program


Functions that are used in only one place, are special purpose, and are usually short (not a requirement, but if you want a long function, it is more common to define it with a name), are often made anonoymously.


Let me go through them again, one sec


[:devices ▼ [:docs ▶️[▼ [:data ▼ ["assignedDate" "12-1-1992"] ["deviceName" "Europe"] ["initialStateLink" ""] ["number" 30] ["owned" "TRUE"] ] [:id "Europe"] [:metadata {:from-cache false, :has-pending-writes false}] [:ref ["device-names" "Europe"]] [:object nil]


There it is straight from the debugger


looks like there is {} around :data


While you were getting that I was working up some code, maybe this will be helpful, one sec…


Caveat: code like that will parse the exact data syntax it’s expecting, but probably won’t be very tolerant of data that fails to match the expected pattern.


Is there a way from the repl to see the artity of a function?


Or even the definition (source just shows the source inside of a function)


Oh looks like doc is good enough.


@jeremy642 (:arglists (meta #'map)) but yeah, the doc probably uses this anyway


@manutter51 Thank you! Let me take a look at it


sneaky code can overwrite the :arglists - sometimes in misleading ways, that's pretty rare though


@manutter51 Thank you for that code, looking at someone else's finished work it looks fairly simple, but starting that for me was tough


Now, when I toss that into my re-frame app, the DB entry looks like this: [:devices [:devices {}]]


So I am assuming there is a mismatch with the [] somewhere


Yeah, I had to tweak it a bit to get everything to thread thru correctly


Yep I understand. Thanks for the foundation, I am going to play around with it now

Mario C.20:07:14

I want to slurp all files in a directory. How would I be able to do this?

Mario C.20:07:51

Currently I am doing (slurp "../path/to/file/my_file.clj")


you might find file-seq helpful, but you'd want to filter out subdirectories if any before slurping


I need a sanity check - if I go into a repl and type (read-string "07") , I get 7 as the result. If I do the same with (read-string "08"), it throws the error NumberFormatException Invalid number: 08 clojure.lang.LispReader.readNumber ( Anyone have any idea why or what I should do instead? I assume there's something in the reader related to octals or something like that that I'm running into.


0 prefix means octal


user=>  010


use Long/parseLong if you know the type ahead of time

user=> (Long/parseLong "010")


Ahh thanks - I guess it's the first time this system has gotten an input that would cause that error. Fun!


you can ask for specific bases with r

user=> 2r1111


user=> 32rclojure


Interesting - one of the parts of Clojure I haven't needed yet. Thanks!


anyway, that means a "10r" prefix on the string would let you continue using read-string but it would be hacky


@manutter51 I realized the data structure I sent to you eariler was wrong, and that is why the snippet you sent me was returning nil 😐 Sorry about that


(get-docs) is returning nil now


Ok, you might be able to apply my approach anyway.


The basic idea is to take the nested data one level at a time. You’ve got a vector with :devices and a nested vector, so write a function to pull out that nested vector (after doing a sanity check to make sure you’re getting the line with :devices in it)


Once you’ve pulled out the nested data, you say, “Ok, this is also a vector with a nested data structure, so I’ll write another function to unwrap that one.”


Ok. How do I pull out data from a set, instead of a vector? The original code assumed the structure was only vectors


This is the real structure now: (def test-data {:docs [{:data {"assignedDate" "12-1-1992", "deviceName" "Europe"}, :id "Europe"} {:data {"assignedDate" "12-1-1992", "deviceName" "Brownie"}, :id "Brownie"}] })


Ok, curly braces is a map, not a set (just to be technical)


Oh right. Set has a hash before it?


So, let’s say you’ve got the test data you just posted. That’s a map with a :docs key, so you can get that with

(defn get-docs [d]
  (:docs d))


Granted it’s a bit of overkill to write a whole function just for that, but for illustrative purposes… 🙂


That's it? Simpler than getting the data from a vector


Yeah, that’s a handy bit of Clojure syntactic sugar


If you want to be more formal about it you can also call (get test-data :docs) so it looks more like a “real” function


but the shortcut is handy


Ok cool, that is handy


Hmmm, get-docs is still returning nil


Now, once you get the value of the :docs key, you get a vector of maps


so probably what you want to do here is write a function that takes just one of those maps and converts it into the format you want. Then you can use map or mapv to iterate over all the maps and reformat them.


I would say try this:

(defn get-docs
  "Extract the :docs data"
  (prn d) ;; print out what arguments are coming in
  (when (contains? d :docs)
    (:docs d)))


Actually, hang on, let me edit that


Ahha no arguments are coming in


Ok, I updated it to be a better fit for the data you posted a few minutes ago. Ok, so if you’re getting no args coming in, probably the function is being called with the wrong args (i.e. none), or else you’re getting back nil from an earlier function call.


Ok I took out get-devices, since there is no :device in the test data


ha. It looks like get-as-map is returning the data in the original structure


Ok I'm going to play with the get-as-map function. Thank you for your time and efforts @manutter51


Thanks! I truly appreciate all of the help you have given me

👍 4
Mario C.22:07:59

Question: I have a project, project-a that I include in another project, project-b. In project-a I have some macros that act like functions. In project-b however, non of these macro functions are available to me. Only the methods that are defined with defn are available. Is this expected behavior? How can I make those macros available to me in project-b?

Mario C.22:07:56

In project-b I am doing (:require [project.a :as pja])

Mario C.23:07:55

The error I get is Exception in thread "main" java.lang.RuntimeException: No such var: pja/my-macro-func


is there a my-macro-func in project.a?


what you say "I am doing (:require [project.a :as pja])" what does that mean?


how do you know project.a has a my-macro-func?

Mario C.23:07:45

In intellij when I type in pja/ it gives me a drop down that I can scroll through and see all functions. The interesting part is that it only list the methods defined with defn but all the methods defined with a macro are strangely missing.

Mario C.23:07:55

I have project-a locally on my machine


and how do you know the project-a you see on disk is the same project-a that project-b is using?

Mario C.23:07:49

I don't which is why I was asking if that is expected behavior

Mario C.23:07:04

Although it should be, to answer your question

Mario C.23:07:47

I meant it should be the same code project-b is using


why should it be?

Mario C.23:07:34

using the same snapshot version I suppose


what have you done to make it possible for project-b to use project-a's code at all?


same snapshot version of what?

Mario C.23:07:46

the project-a on my local machine is simply for reference. project-b is calling 'project-a' that is on some s3 bucket I believe


why should what is in the s3 bucket match what you have locally?

Mario C.23:07:58

In project-b project.clj file it has a specific version defined as its dependency

Mario C.23:07:17

I pulled that code from github


a specific version or a snapshot?

Mario C.23:07:23

switched to that version


snapshots are sort of the opposite of a specific version


maven doesn't care to some degree, and to some degree treats snapshots as special when resolve dependencies, and developers treat snapshots as special


snapshots tend to be overwritten, so the "same" snapshot will point to different bits of code at different times

Mario C.23:07:14

I see so this is issue is more of a "project-a probably doesn't have the methods I think it has"?

Mario C.23:07:31

In other words macros are visible in other projects?


maybe, hard to say, but is a distinct possiblity

Mario C.23:07:47

I guess the best way to figure this out is to use project-a that is on my local machine


or use a specific version of project-a in project-b


or look at the classpath for project-b to find the jar for project-a that it is using, and look inside the jar to see what is there

Mario C.23:07:55

Thanks! Looks like I have no choice but to do that lol


you might want to start even with looking in project-b's repl to see what is defined in the namespaces from project-a

Mario C.23:07:48

Thats what I did

Mario C.23:07:03

And non of the methods defined with a macro were visible


what do you mean "defined with a macro" and what do you mean by "methods"?

Mario C.23:07:18

In project-a we have a file that has various methods defined as (defn example [] (println "hello"))

Mario C.23:07:10

But we also have a macro that defines functions* as (defmymacro my-macro-fun ...)


how do you know the macro is correct?

Mario C.23:07:27

Those that are define with that are not visible


just because the macro has 'def' in the name doesn't mean it defines anything


(it should, but there is no accounting for taste)


also, just because a var is defined doesn't mean cursive documents it / shows it properly


I've had complaints about defining vars with custom macros from cursive using coworkers

Mario C.23:07:58

Silly mistake


ah, right cursive does static analysis and doesn't recognize the custom defmymacro

Mario C.23:07:46

If I spent a little longer reading the docs I wouldn't have wasted time figuring this out lol

Mario C.23:07:53

I needed to use another function to execute them

Mario C.23:07:34

Thanks guys