Fork me on GitHub
#beginners
<
2018-07-23
>
beders13:07:38

Looking for feedback on a set of slides I put together (I'm a clojure newbie myself). https://slides.com/jochenbedersdorfer/clojure-for-java-developers#/

kennytilton13:07:12

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?

kennytilton13:07:26

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 :)…

beders14:07:30

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

beders14:07:43

oh, and thanks for your feedback, btw!

kennytilton14:07:44

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:

dpsutton13:07:14

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

😀 1
👍 1
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!!!

agile_geek14:07:09

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.

agile_geek14:07:31

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

agile_geek14:07:30

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

agile_geek14:07:53

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? https://clojars.org/search?q=ipv4+validation 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.

jonahbenton14:07:08

The guy who started golanglibs made one for clojure: https://clojurelibs.top/

jonahbenton14:07:02

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.

jonahbenton14:07:24

In clj, think of validating data shape not type.

jonahbenton14:07:30

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

jonahbenton14:07:20

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

jonahbenton14:07:00

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?

cvic15:07:21

Maybe play with both? But spec is the future tbh

jonahbenton15:07:50

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.

jonahbenton15:07:28

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.

lepistane14:07:55

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

lepistane14:07:05

can't make sente work

lepistane14:07:15

most of the code is copied from official example

lepistane14:07:20

but i can't send

lepistane14:07:55

on test app i added it and it works

lepistane14:07:16

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

mikeyford14:07:03

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?

andy.fingerhut15:07:50

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.

andy.fingerhut15:07:57

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.

andy.fingerhut15:07:41

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.

mikeyford15:07:22

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

donaldball15:07:33

Tom Crayford’s Performance and Lies talk is a good overview of some of the challenges in benchmarking on the JVM: https://www.youtube.com/watch?v=0tUrbf6Uzu8

Aaron17:07:21

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

Chase17:07:36

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.

kennytilton20:07:38

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)))

Chase17:07:15

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

Aaron17:07:59

@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

andy.fingerhut17:07:33

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.

Aaron17:07:33

Let me go through them again, one sec

Aaron17:07:24

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

Aaron17:07:30

There it is straight from the debugger

Aaron17:07:17

looks like there is {} around :data

manutter5118:07:54

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

manutter5118:07:20

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.

jeremy18:07:17

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

jeremy18:07:53

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

jeremy18:07:44

Oh looks like doc is good enough.

mfikes18:07:59

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

Aaron18:07:23

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

noisesmith18:07:36

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

Aaron18:07:35

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

Aaron18:07:00

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

Aaron18:07:26

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

manutter5118:07:59

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

Aaron18:07:50

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")

noisesmith20:07:23

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

shaun-mahood20:07:58

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 (LispReader.java:342) 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.

noisesmith20:07:11

0 prefix means octal

noisesmith20:07:29

user=>  010
8

noisesmith20:07:21

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

user=> (Long/parseLong "010")
10

shaun-mahood20:07:03

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

noisesmith20:07:17

you can ask for specific bases with r

user=> 2r1111
15

noisesmith20:07:45

user=> 32rclojure
13615364974

shaun-mahood20:07:34

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

noisesmith20:07:12

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

Aaron21:07:39

@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

Aaron21:07:18

(get-docs) is returning nil now

manutter5121:07:48

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

manutter5121:07:54

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)

manutter5121:07:46

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.”

Aaron21:07:46

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

Aaron21:07:09

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"}] })

manutter5121:07:33

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

Aaron21:07:47

Oh right. Set has a hash before it?

manutter5121:07:04

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))

manutter5121:07:37

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

Aaron22:07:03

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

manutter5122:07:33

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

manutter5122:07:06

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

manutter5122:07:24

but the shortcut is handy

Aaron22:07:44

Ok cool, that is handy

Aaron22:07:10

Hmmm, get-docs is still returning nil

manutter5122:07:10

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

manutter5122:07:15

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.

manutter5122:07:38

I would say try this:

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

manutter5122:07:24

Actually, hang on, let me edit that

Aaron22:07:30

Ahha no arguments are coming in

manutter5122:07:15

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.

Aaron22:07:58

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

Aaron22:07:34

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

Aaron22:07:54

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

Aaron22:07:37

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

👍 1
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

hiredman23:07:56

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

hiredman23:07:34

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

hiredman23:07:09

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

hiredman23:07:27

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

hiredman23:07:58

why should it be?

Mario C.23:07:34

using the same snapshot version I suppose

hiredman23:07:34

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

hiredman23:07:46

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

hiredman23:07:13

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

hiredman23:07:22

a specific version or a snapshot?

Mario C.23:07:23

switched to that version

hiredman23:07:10

snapshots are sort of the opposite of a specific version

hiredman23:07:03

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

hiredman23:07:35

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?

hiredman23:07:37

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

hiredman23:07:14

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

hiredman23:07:57

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

hiredman23:07:47

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

hiredman23:07:45

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 ...)

hiredman23:07:25

how do you know the macro is correct?

Mario C.23:07:27

Those that are define with that are not visible

hiredman23:07:49

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

hiredman23:07:12

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

noisesmith23:07:21

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

noisesmith23:07:44

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

Mario C.23:07:58

Silly mistake

hiredman23:07:20

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