Fork me on GitHub
#beginners
<
2018-04-07
>
suryabinary02:04:47

What would I do this in clojure : num = 1 while ( num < 100){ if(num % 3 || num % 5 == 0){ fmt.Printf(num) } }

dogenpunk02:04:41

(doseq [n (filter #(or (= 0 (rem % 3)) (= 0 (rem % 5))) (range 1 100))]
        (prn n))

suryabinary02:04:55

A bit verbose, senor ?

aengelberg02:04:45

another way to do it:

(doseq [n (range 1 100)
        :when (or (= 0 (rem n 3)) (= 0 (rem n 5)))]
  (prn n))

suryabinary02:04:27

What's a doseq

aengelberg02:04:45

It iterates through a collection

suryabinary02:04:38

@aengelberg gracias !

suryabinary02:04:57

Can u give me an example of a macro

suryabinary02:04:01

A very simple one

aengelberg02:04:37

Here's an example of a macro that does nothing

(defmacro nothing-to-see-here
  [inner-form]
  inner-form)

(nothing-to-see-here (+ 1 2))
=> 3

aengelberg02:04:51

Here's an example of a macro that does something but is very simple

(defmacro infix
  "Turns infix notation into prefix notation"
  [form]
  (list (second form) (first form) (nth form 2)))

(infix (1 + 2))
=> 3

suryabinary02:04:33

I read on the internet that macros add additional functionality to clojure code

aengelberg02:04:34

They're helpful for providing shorthands for common expressions

suryabinary02:04:55

I'm a beginner to clojure and still finding my way around the ()

suryabinary02:04:08

But this is a very unique and cool language

aengelberg02:04:15

That's true 🙂

suryabinary02:04:38

Yuppppppppp...

suryabinary02:04:06

And :

(while (< a 10) (println a )) 

suryabinary02:04:31

Can be done like

(while (< a 10)

(println a)) 

suryabinary02:04:38

For readability?

aengelberg02:04:41

that probably wouldn't work because you aren't incrementing a anywhere

aengelberg02:04:50

so it would get in an infinite loop

suryabinary02:04:56

Oh yea ..i forgot

suryabinary02:04:00

Infinite yes..

aengelberg02:04:43

but yeah, adding newlines usually improves readability, as long as you indent the inner forms. So the best way to style that code would probably be:

(while (< a 10)
  (println a))

suryabinary02:04:04

I plan to use clojure for general purpose...

suryabinary02:04:09

To make small utilities

suryabinary02:04:16

Will it fit in ?

aengelberg02:04:27

cool, what kind of small utilities?

suryabinary02:04:47

Like cache cleaners ,file finder ..

suryabinary02:04:19

Utilities that I'm missing on my laptop

suryabinary02:04:44

I was about to do it in go..but then I'm liking clojure a lot more

aengelberg02:04:22

Clojure is very expressive but has a long startup time

aengelberg02:04:40

If you use it to write scripts you might find yourself waiting a couple seconds after typing any command

aengelberg02:04:08

But you might want to check out ClojureScript

aengelberg02:04:37

There are a couple tools available that make it especially easy to write command line scripts in CLJS, including Planck http://planck-repl.org/ and Lumo https://github.com/anmonteiro/lumo

suryabinary02:04:20

Coooool...this is awesome

suryabinary02:04:34

I was thinking of making jars as my utilities

suryabinary02:04:45

lein uberjar stuff

aengelberg02:04:59

I've done that for a couple of my scripts, where I don't care about the couple second wait

theeternalpulse03:04:26

I've been looking at lumo, which provides a clojurescript environment based on node

theeternalpulse03:04:50

if you don't mind not having java interop, I think this might be a better solution

suryabinary03:04:01

So I need to learn clojurescript..

suryabinary03:04:10

Is it a lot different from clojure

aengelberg03:04:17

It's very similar actually

theeternalpulse03:04:19

most of the core language is the same

aengelberg03:04:58

All of the core functions are the same, it only is different if you do low-level interop with the host language (Java or JavaScript)

aengelberg03:04:49

But if you're comfortable with lein I'd recommend getting started on that and using uberjars, then later you can switch to ClojureScript if you want some extra speed

suryabinary03:04:08

I'm comfy with lein

suryabinary03:04:32

I thought clojurescript was only for web development

aengelberg03:04:16

people usually use it for frontend browser stuff, but some people have started using it for server-side development, writing scripts, and mobile apps

suryabinary03:04:54

The jar made by lein would be pretty fast right ?

suryabinary03:04:05

If compared to a script with clojure ?

aengelberg03:04:49

probably, or if you add :aot :all to the project settings (ahead-of-time compile) that helps too

suryabinary03:04:23

Where should I add :aot :all

seancorfield05:04:55

Looks like no one answered this. If you created your project via

lein new app surya
it'll already by in the :profiles section under :uberjar:
(! 1078)-> cat project.clj 
(defproject surya "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main ^:skip-aot surya.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

suryabinary03:04:27

I'm using vim

dadair05:04:09

I’m getting a weird clojure.test failure when equality is somehow failing for something that is equal when I test it in the repl.. anyone know what could be going on here?

expected: {:topic {:id "some/topic", :args {}},
           :event {:id "some.topic/event", :payload {}},
           :req nil}

  actual: {:topic {:id "some/topic", :args {}},          
           :event {:id "some.topic/event", :payload {}},
           :req nil}
    diff: - nil          
          + nil

dadair05:04:53

The result of using clojure.data/diff is [nil nil <the map>], so they should be equal, no?

mfikes05:04:54

It makes you wonder if one of the nils is actually (symbol "nil")

dadair05:04:40

I get the same failure doing select-keys on both maps to only select :topic

dadair05:04:59

I’m testing a deserialization round trip here, so technically the one map is a standard record, and the other is the record serialized to json, and deserialized back into the record

mfikes05:04:38

@dadair Are you using Transit to serialize a record?

dadair05:04:21

No just straight clojure.data.json

mfikes05:04:32

Well, if on one side it is a record and the other side it ends up being a persistent hash map, that would do it. (They won’t be equal.)

mfikes05:04:36

For example, (defrecord Foo [a b]) then (= (->Foo 1 2) {:a 1 :b 2}) is false

dadair05:04:41

ah yeah that’s the problem, the internal topic/event maps are records, and after deserializing the outer one is a record but the inner ones aren’t, thanks!

klaus.azesberger08:04:11

hi, and thanks for all the helpful ppl here (already learned so much just by lurking this channel) my first question: "can you explain me why ppl are so happy with the REPL? My first impulse is/was that i miss the possibility to use a debugger to step very finegrained through every part of my code, which afaik is not possible with clojure functions? On the other hand, using the REPL: isn't this very tempting to not apply 'hammock driven dev' and instead hack your way with small iterations to the final solution? ...I'd still miss the debug step over feature because I'm new and would like to have something to see how stuff works."

ackerleytng09:04:21

are you using emacs and cider? if you are, the cider debugger is really amazing with debugging

ackerleytng09:04:30

i learnt a lot from this.

sundarj09:04:36

using a REPL doesn't conflict with hammock-driven development; it compliments it

sundarj09:04:01

also worth checking out: http://bpiel.github.io/sayid/ (a debugger for Clojure)

val_waeselynck09:04:14

@ 2 shameless plugs: 1. As mentioned by @sundarj, the REPL guide (https://clojure.org/guides/repl/introduction) is meant to address these points, please give feedback if it doesn't suit you. In particular, see https://clojure.org/guides/repl/enhancing_your_repl_workflow#debugging-tools-and-techniques 2. I wrote scope-capture partly to help beginners see how stuff works, interested in feedback here as well https://github.com/vvvvalvalval/scope-capture

val_waeselynck09:04:17

> using the REPL: isn't this very tempting to not apply 'hammock driven dev' and instead hack your way with small iterations to the final solution? That is true of any tool that gives rapid feedback (including traditional debuggers, and advanced editors). Giving up on such tools for this reasons seem a bit extreme IMO 🙂

klaus.azesberger11:04:45

thank you all so much! need to delay further feedback as my son just woke up from his nap 🙂

leongrapenthin12:04:15

In short: If you are used to the write/compile/debug workflow the REPL will stimulate your creativity but also suck you in and exhaust you quickly because you are not used to such an immediate programming workflow. Remember to take a break every now and then and think about what you are trying to do. Regarding debugging look at Stuart Halloways talk "Debugging with the scientific method"https://www.youtube.com/watch?v=FihU5JxmnBg

theeternalpulse14:04:00

TBH repl driven development has more visible payoff and is far more useful than Test Driven Development. Having immediate feedback without having a formal, "Test, fail, implement, fix" is what most developers want to do naturally. I think it's far more natural to want something that gives you immediate feedback without having a formal process of testing.

val_waeselynck15:04:53

@ no reason to choose between both though

theeternalpulse16:04:27

perhaps, but I've never found in any language TDD a useful solution to any problem that I'd have later on. RDD (Repl Driven Development) has helped me far more than anything I've had in the past (mainly having it integrated in my ide).

theeternalpulse16:04:03

The tools that mimic that such live coding as well have more value to me than writing a test

theeternalpulse16:04:25

wrt other languages such as elm and javascript/webpack

klaus.azesberger21:04:53

@ wow your scope-capture (https://github.com/vvvvalvalval/scope-capture) is awesome! This will certainly help me a lot. ✔️starred and followed @ thanks for this awesome video. learned a lot from this too, gonna revisit this video in 1-2 weeks probably as i feel like i didn't grasp everything entirely yet. right now I'm gathering all this useful stuff to be able to condense a short introduction of some basic concepts to my (almost pure java) colleagues at work. thank you all again for the answers!

val_waeselynck08:04:06

@ I don't know if that's what you meant, but I would advise against giving up on tests just because you have a REPL. The benefits of a REPL are ephemeral, and not shared. As for REPL "Driven" Development, I don't think REPLs can drive any more than tests - only a thoughtful developer can drive. Again, just expliciting my thoughts here, not trying to put words in your mouth

theeternalpulse17:04:57

I don't mean give up testing, I jsut don't find the TDD workflow useful and in fact for me personally it has only slowed me down. I have heard the arguments up and down and tried it and in practice it does nothing for me that the REPL does. I also keep a sandbox of my personal code that does anything significant or novel so it's not so ephemeral.

theeternalpulse17:04:59

I guess I also don't want to promote using the repl primarily over tdd, but for me I've just not had TDD click as much as having immediate feedback in line with code I'm writing.

clojurians55811:04:10

hi, when switching namespaces in a repl (emacs/cider) I don't get completion for symbols inside the current namespace, any ideas why ? I do get completion if I fully qualify the symbols though (prefixing the ns)

clojurians55811:04:50

it is as if the repl doesn't know I am inside that namespace

matan12:04:36

So it's often said or written "clojure programming focuses on many functions over a small group of data types" or something like that isn't it?

matan12:04:15

Well I've never felt that so far, is there a good case in point for this general claim often waived at when trashing object oriented? so far I only see the advent of spec as disproving that some sense of type safety is unimportant for basic productivity, whereas I am not feeling the gist of many functions operating on data being very scalable in projects of mine; nor do I see it very immediately evident elsewhere.

matan12:04:17

All languages have their initial hype/followers half-truths, but I feel I might be just missing a good case in point here...

tbaldridge12:04:08

@matan you're touching on two different things here, one is the concept of how we change data, and the other is how we identity data.

tbaldridge12:04:03

Predicate based data validation (like spec) is about identifying data. (valid? :person {:name "foo"}) is a way of saying "is this data valid as a representation of a :person"

tbaldridge12:04:52

The idea of "many functions on the same bit of data" is about how we manipulate data. And the idea there is that I should be able to walk up to any hashmap and say (for example) (assoc some-map :foo 42). That's not something I can do in most OOP languages. In python walking up to any object and adding a new attribute to it can have all sorts of side effects.

tbaldridge12:04:45

But in Clojure the idea is also that "extra data" shouldn't matter. If you hand me a hashmap, and I add new keys/values to it, I should be able to hand it back to you and you shouldn't care that there's new data attached. This is huge for the work I do. Using namespaced keys I will often merge hashmaps together as the program calculates more information. In order to do this in OOP, I'd have to create a new object class for every combination of hashmaps in my program. In Clojure I simply do (merge map1 map2)

mfikes13:04:13

An argument I’d make is, as you scale up to large codebases, there is a nice uniformity in the sense that if you are working with some value returned by some function you didn’t write, all of the core functions and their behaviors apply to that data. Compared to Java, for example, you are often put in the position of first having to understand the bespoke accessors and other functions that might be on the object handed to you.

matan13:04:31

@ please kindly help me get this. with Java, I can always bundle objects into a collection of my choice, but of course if I want to change a particular type I have to understand how my change would affect it, hence the not-blackbox approach kind of justified isn't it? This perfectly corresponds with my reply to @tbaldridge the same point and pondering really

mfikes13:04:04

So, say I have a co-worker on the same dev team with me. That co-worker authors the code for some fancy object. Perhaps it has a collection of things in it, and that co-worker chooses to expose a method named .itemCount. The simple fact that I have to grok that non-standard method to learn how many things are in it is the problem.

mfikes13:04:58

I think the phrase used to describe this problem is that “each object comes with its own little language” you have to learn.

matan13:04:52

@tbaldridge thanks for commenting! This I perfectly get: > In order to do this in OOP, I'd have to create a new object class for every combination of hashmaps in my program. But > and I add new keys/values to it, I should be able to hand it back to you and you shouldn't care that there's new data attached. Is too easy to violate isn't it? it's enough that the other function iterates over keys and it may break in ways internal to what it's trying to do when iterating the keys, once you've added keys to it, no?

matan13:04:34

Or is this all to say, I should make effort to avoid writing functions that care whether or not someone else added keys to the maps they operate on?

tbaldridge13:04:26

The latter, it's quite rare that you'd need to iterate over keys and care about what they contain. Most of the time when you iterate you are doing something that's generic enough that you don't care what the values are. But more commonly you'll simply pull out the keys you care about.

tbaldridge13:04:38

And namespacing keys helps a lot here. that way you can have :person/name and :city/name in the same hashmap

matan15:04:48

@tbaldridge Thanks. I guess have to be very careful in designing my clojure functions to comply with that... this is more by-convention than taken care of by the core. I guess when people ask about design patterns in clojure ― this mertis the first place (or at least some examples and counter-examples).

tbaldridge15:04:48

I doubt you’ll have much problem, so much of Clojure conforms to this pattern that it’s rather easy to implement.

matan15:04:18

I admit to having written functions that do care about what keys data contains....

matan15:04:48

It practically means any free-form dictionary structure should not be naively embedded in a clojure map doesn't it? what about structured data, how does the rule work there? I'm really, in a way, perplexed

noisesmith15:04:26

@matan I'd say the opposite - if you only look at keys you know you care about, and ignore the rest, that is a basis for being able to use free-form dictionaries

noisesmith15:04:40

there's also things like group-by where you don't know what the specific keys will be but they tell you something about some other data source - that's different

matan16:04:48

it sounds hard to code to this standard ― I mean easy in small programs, but as things scale, inappropriate reliance on keys might seem a convention hard to enforce, especially across large teams!

matan16:04:11

by the way a case in point ― dynamically generated keys!

lee.justin.m16:04:50

my biggest problem with the open map philosophy (which is reflected in spec) is that it doesn’t let me quickly detect the most obvious and frequent kind of error: fat-fingering a key name. the presence of an unexpected key is almost always a mistake on my part

erhardt.mundt17:04:26

I'm playing around with boot and I'm having problems getting an uberjar for my project

erhardt.mundt17:04:49

Compiling 1/1 autolocker.core...
Writing pom.xml and pom.properties...
Adding uberjar entries...
Writing autoclocker-0.1.0.jar...

erhardt.mundt17:04:12

that's the output when I execute $ boot uberjar

erhardt.mundt17:04:33

I was expecting to see a target folder in the root project folder, but the folder doesn't get created and I can't find autoclocker-0.1.0.jar

sundarj17:04:37

@erhardt.mundt boot doesn't write any files until you run the target task. so run boot uberjar target and your uberjar should be written to the target directory

erhardt.mundt17:04:05

oh, good to know

erhardt.mundt17:04:18

thanks @sundarj

erhardt.mundt17:04:54

@sundarj I only see .class files in the target folder, I guess I should also include .clj files right?

sundarj17:04:20

that i do not know i'm afraid

erhardt.mundt17:04:06

no worries, thanks anyway 🙂

prozz18:04:17

hi, i have scala application that use akka actors and akka streams. any advice on how to easily achieve similar functionality with use of clojure?

tbaldridge18:04:30

A lot of that depends on what your final goal is with then application

gklijs18:04:42

@prozz you might want to take a look at #onyx

tbaldridge18:04:05

In general actors tend to be a bit over-hyped as a tech. Instead Clojure breaks up concurrency and distributed concerns into many different primitives. Clojure's official view on actors can be seen here: https://clojure.org/about/state#actors

tbaldridge18:04:34

But I've personally used CSP via core.async, OS threads with a distributed queue, async web servers, etc.

tbaldridge18:04:44

The overall view here is "pick the right tool for the job".

prozz18:04:47

yea, but its quite hard to evaluate what tool is right when u start fresh

prozz18:04:06

i guess ill need to start with small problems and build up my experience first

prozz18:04:48

thanks for suggestions @tbaldridge @gklijs

tbaldridge18:04:46

@prozz that's true, but I think simple is the place to start. Ask "do I need feature X for this application". For example, async web servers are nice, but they're also slower in some aspects than webservers that use a dedicated OS thread. It's quite possible to service a large number of connections without going async. Likewise most SQL servers are not async internally, and many aren't even at the client level. So if your app is mostly concerned with CRUD operations, having async code won't help as much.

tbaldridge18:04:33

So there's a balance there, I'd say try porting part of the app to Clojure in the simplest way, and then see where you come in performance wise. Sometimes the bottlenecks aren't even in the application, and instead are in the DB or in the client.

orestis18:04:20

How would an async web server be slower than using an OS thread? Genuinely curious.

tbaldridge18:04:58

Async involves the overhead of context switching. Whenever you're waiting on IO the system has to suspend the light-weight thread (actor or whatever) and then release the thread back to the OS. Once IO completes something has to go back and pick up the work that was paused. With a dedicated OS thread all this happens in the kernel which can be quite fast. Also these async systems often involve some sort of synchronization, either channels, or message boxes, or something. All that extra book keeping adds latency to the response time of a single thread.

tbaldridge19:04:12

So going async is more about reducing memory usage (by having less OS threads) and therefore packing more requests into a single box. The total throughput of the system may be higher since most of the time was spent waiting on IO. Perhaps 8 cores could service 1024 requests at a time in this example. But the overall processing time of each of these requests will be higher than if you had a single OS thread dedicated to that single request.

tbaldridge19:04:42

Another aspect is that there are parts of these systems that are "interpreted" to some extent. Erlang uses pattern matching to dispatch on the contents of a message, those dispatches could probably be direct function calls if async wasn't needed. Due to the way core.async works code inside the body of a go runs at about 50% the speed of normal Clojure code.

orestis19:04:59

Thanks for the summary! It seems that lately the push in the web world is towards more concurrent connections vs individual speed.

prozz19:04:14

that's some interesting points, nice!

orestis19:04:26

I was thinking lately that a lot of small scale systems or internal tools, there’s a huge amount of complexity that can be eliminated.

orestis19:04:26

If you know that your system will only support 100 or 1000 users, you can get away with extremely simple designs.

tbaldridge19:04:44

It's very true, and even in some large systems. I worked on a receipt pipeline for a very large retailer. Once we realized what it would take to make the whole system run well with async code, we scrapped the idea. Ran about 64 threads on each server and spun up about 100 servers. Our "fail fast" method was to do System.exit and restart the JVM whenever we hit a critical bug. The whole thing communicated via a distributed queue. And it all worked very well. We might have gotten better throughput with async code, but we would have had a lot more complexity.

orestis19:04:30

How many cores for the 64 threads?

tbaldridge19:04:02

I don't remember, I think it was 32, so roughly 2x threads for cores

orestis19:04:54

I would be extremely interested in any resources that deal with distributed systems in the JVM world. Erlang hides away some of the details but I feel that if you start pushing the system you end up having to know about the trade offs anyway.

seancorfield19:04:10

@prozz We used Scala for a while at work before switching to Clojure. We had a process that used actors heavily (the built-in ones in 2.7/2.8) and when we switched it to Clojure, we achieved the performance we needed with just a couple of pmap calls instead (in fact, at one point we had to refactor to remove a pmap call because we were overwhelming an external system that we interacted with). It's often surprising just how much performance you can get from a simple, basic design, without jumping for the concurrency primitives at all.

tbaldridge19:04:04

@orestis it really depends on the design. I think Erlang is great for what it was created for: high throughput of small messages. That receipt pipeline we built handled about 1200 msg/sec sustained. But those messages were 4-10KB each. The original use case of Erlang was somewhere in the range of a few hundred bytes. Go read some of the stuff behind the LMAX Disruptor and the work of Martin Thompson and he's worked on systems that push close to 1mil msgs/sec on a single machine. But those messages are mostly stock related. So somewhere in the range of a few dozen bytes.

orestis19:04:46

Thanks @tbaldridge

loganpowell22:04:35

Hi everyone, thank you again for the help last time. I'm back with more newb questions: I'm trying to use a Java static method as a predicate/argument:

(defn reduce [f result coll]
  (if (not= '() coll)
    (reduce f (f result (first coll)) (rest coll))
    result))

(defn filtering [pred]
  (fn [rf]
    (fn [result el]
      (if (pred el)
        (rf result el)
        result))))

(reduce ((filtering Character/isLetter) conj) '() '(\H \e \l \l \0 \!))
I stole the HOF from a gist and it works fine if I use odd? or something like that, but trying to use Character/isLetter throws:
CompilerException java.lang.RuntimeException: Unable to find static field: isLetter in class java.lang.Character
I did find some clarity on the Java Interop page: https://clojure.org/reference/java_interop as well as this SO post: https://stackoverflow.com/questions/35199808/clojure-unable-to-find-static-field However, these don't give any hints as to how else Java static methods/fields can be used (i.e., passed as arguments to other functions)... Any pointers?

schmee22:04:19

@loganpowell the standard way is to wrap it in an anonymous function: #(Character/isLetter %)

loganpowell22:04:52

@schmee You have no idea how long I've been bashing my head on this. Thank you so much ! 🙏

schmee22:04:07

no worries, that one took me a while to figure out as well 😄