beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2020-12-19T08:06:09.494300Z

Is there like a support group to talk to about seeing - truly seeing - Clojure for the first time? As someone who haven’t done LISPs before, but have extensive C# experience as well as a great interest in typed functional programming, I never thought a LISP with dynamic (!!!) typing would be anything for me. But having just worked through a couple of Advent of Code challenges I feel obsessed with the language. I dream about it! Lovely parens everywhere! Watching some of Rich Hickey’s talks just jolts me with insights that challenge my last ten years of professional programming experience. Seeing static types as a lovely puzzle to solve, but oftentimes a hindrance to actually solving problems - 🤯 In many ways using Clojure feels like the first time I learned Python some 20 years ago. Everything feels exciting and challenging and possible. Things just make sense. The first time ever writing a single line of Clojure I completed twelve programming challenges in Advent of Code without getting the answer wrong a single time. No off by ones, no confused abstraction usage. No null ref bombs. Just data and (already familiar) functions, packaged up in a way that feels intuitive and easy to use. No “killer feature” that actually involves a lot of complexity (looking at you, Go channels) and leads beginners down a path of power but also confusion.

12👍3
simongray 2020-12-21T13:51:21.153700Z

Welcome to the cult. Types are overrated.

2020-12-23T18:50:55.322600Z

> No “killer feature” that actually involves a lot of complexity (looking at you, Go channels) and leads beginners down a path of power but also confusion Well 😅, actually there are some of that, but lets say they're kept away for advance power users. You've got the same complex channels and CSP Go-routines, actually probably even slightly more complex than in Go with core.async. And you've got user defined macros I'd say are an easy way for beginner to get themselves in a place of too much power that confuses them. But yea, the language steers you towards simple things, that's really engrained to its core. So happy you're enjoying it! I've had the same happen to me, and now no other language brings me Joy anymore, only Clojure or Clojure-like languages do 😛

Panagiotis Mamatsis 2020-12-19T10:59:27.496500Z

Hello everyone! I hope my message finds you and all your loved ones well and healthy! I am in Clojure for three months now. Of course i am still a complete n00b but i wanted to ask a question...how Clojure compares to Scala? I mean they are both functional but any differences?

2020-12-23T18:56:54.323100Z

I think @flowthing explained the difference pretty well. I would say that in my experience, a lot of people actually use Scala in an imperative OO style, and not functional, because a lot of people choose it over Java simply to avoid the verbosity that Java has (or used to have, since its improved a lot in that area more recently). This isn't always true, die hard passionate Scala devs do use it mostly functionally, but in my work experience, places that are JVM shop (and not Scala shop specifically), where you have some teams or services that tried Scala, often end up using it in OO style. Clojure on the other hand can't be used like that. So its both a harder transition, as well as a better introduction to FP in my opinion.

2020-12-23T18:58:45.323300Z

Clojure can do hot-code reloading, and dynamic code loading as well. But I'd say, in Erlang, you might realistically (though even in Erlang its not really done), deploy your code changes to prod using hot-code reload. Where as in Clojure, you probably shouldn't deploy changes with hot-code reload to prod, but you will definitely use it for development, testing, and debugging.

2020-12-23T19:10:08.323600Z

Oh, one more difference with Scala, I have found, Clojure has much better interop with Java, where as Scala's interrop requires a lot more hoops, so I find Scala to be more of an island.

emilaasa 2020-12-19T11:47:16.496800Z

I know the feeling! 🙂

flowthing 2020-12-19T12:41:29.497100Z

Plenty of differences. I'd say that the only similarities are that they are both functional languages on the JVM. Scala is a statically-typed language with a powerful (and complicated) type system. It's also a multi-paradigm language. That means you can use in both imperative and functional style. Most Scala users learn towards the functional style, in my experience. Clojure, in contrast, is a dynamically-typed language. You can use it for imperative programming, but very few people do that, and the language heavily steers you toward functional programming. Clojure is also a Lisp. Clojure developers typically make heavy use of the REPL to interactively iterate on their programs. Scala is more traditional in that sense, relying heavily on static typing and IDE support. Clojure is a vastly simpler language than Scala. See also the Clojure rationale at https://clojure.org/about/rationale.

1👍
flowthing 2020-12-19T13:06:50.000600Z

Oh, and importantly, Clojure puts data first, whereas Scala is more reminiscent of the traditional object-oriented approach in that respect.

1👍
2020-12-19T13:27:42.001600Z

Is Clojure somewhat slow on doing lots of mathematical calculations? Currently I'm summing 500k vectors of ~20 numbers and that takes almost a second in total. Is that expected performance or am I doing something wrong? Essentially just mapping (reduce +) over a for-comprehension. This code takes 100 ms to run in my REPL, so I'm guessing I'm doing something weird in my original code...

(time (reduce + (map (partial reduce +) (map #(range % (+ % 25)) (range 500000)))))

2020-12-23T19:12:41.323800Z

Everyone else assumed that you're hitting the bottlenecks of boxed math. But I don't think that is what is happening in your case. Are you sure you're not measuring the startup time as well?

2020-12-23T19:36:16.324Z

But, if the slow-down is because Clojure persistent data-structures and sequences do boxing of primitive types, I recommend this article: https://neanderthal.uncomplicate.org/articles/fast-map-and-reduce-for-primitive-vectors.html If you really want fast numerical computation, and I mean, faster than Java fast, Neanderthal and Fluokitten are what you want!

1❤️
Panagiotis Mamatsis 2020-12-19T13:46:11.001900Z

Hi @flowthing and thank you so much for your response! My background is mostly JVM languages. For the last 4 years i am working on Groovy language. I have touched a bit of Scala but never truly kept me due to the complexities that you are referring. I felt that i wanted more power and more simplicity! That's why i have chosen to start learning Clojure. I have a long way to! One more thing that hooked me is the ability of this language to do hot code swap. Something that, if i am not mistaken, only the BEAM languages can do.

Ben Sless 2020-12-19T13:53:41.002100Z

Well, let me tell you about boxed maths https://insideclojure.org/2014/12/15/warn-on-boxed/ https://clojuredocs.org/clojure.core/*unchecked-math* The performance hit is about 100x

valerauko 2020-12-19T13:54:12.002500Z

Clojure actually has async channels though

Ben Sless 2020-12-19T13:55:21.002700Z

that's the first thing to be aware of

Ben Sless 2020-12-19T13:57:35.002900Z

but we can do some more digging

Ben Sless 2020-12-19T14:00:31.003100Z

The code is very functional but slightly abuses lazy sequences, can we eliminate some of that overhead?

Ben Sless 2020-12-19T14:13:15.003300Z

You'll gain a slight speedup by dispatching directly to more specific methods. Def-ing something like

(defn qadd
  ([] 0)
  ([^long x] x)
  ([^long x ^long y]
   (unchecked-add x y)))
and using it instead of + will already improve performance

Ben Sless 2020-12-19T14:23:27.003500Z

Should also take a look at https://clojure.org/reference/java_interop#typehints

Timur Latypoff 2020-12-19T15:10:50.003800Z

I agree with @ben.sless. As far as I understand, for best performance, intensive numeric calculations should be done in a tight (loop [...] ... (recur ...)) within a single function (Clojure fns box their args) with typed non-boxed locals and typed arrays (no lazy sequences, no Clojure vectors since they store boxed Objects).

1☝️
Ben Sless 2020-12-19T15:38:14.004400Z

Definitely, a loop would be best, I tried not to break the initial mold too much. All the ranges do take their toll

2020-12-19T15:54:31.005700Z

@ben.sless Sorry for disappearing, my daughter woke up and then real life happened. Thanks for taking the time. Good to know that there’s an escape hatch when that type of performance is needed!

2020-12-19T15:55:15.007200Z

My assumption was that laziness would help speed up the process in terms of not having to realise all the sequences in memory before performing computation. Obviously that’s not a correct assumption based in your input...

Ben Sless 2020-12-19T16:08:12.007400Z

@anders152 I'm in a similar situation, don't feel pressured to synchronously reply 🙂 The full answer is that like in every case, it depends. You could create a 2d array and sum it, that would be fastest. Searching on both dimensions of laziness and allocations until we find an optimum solution is an interesting exercise, for which results may vary depending on anything from your implementation, to your JVM version and choice of GC algorithm

2020-12-19T16:40:10.010400Z

Thanks a lot for all the input! I don't really need to solve this specific performance issue, I just wanted to understand what I could do if this issue crops up in a real case.

2020-12-19T16:46:14.011400Z

The boxing explains a lot, though. As long as it’s possible to avoid it and optimize for speed over safety or ergonomics in the few cases where it’s needed I’m happy.

2👍
Ben Sless 2020-12-19T16:56:26.011800Z

There's nothing wrong with having a few scary, well tested and non-idiomatic functions which deal with unboxed data and perform black magic

Ben Sless 2020-12-19T16:59:06.012Z

But there's always a risk if anyone besides yourself might use them

dorab 2020-12-19T17:00:47.012200Z

For a slightly more advanced example, and a very good explanation of the process behind such optimizations, take a look at a recent conversation on clojureverse (in particular, the answer by joinr) https://clojureverse.org/t/my-recent-clojure-vs-c-experience/6909/3 Though, doing unboxed math, using arrays, using loop/recur and not using lazy sequences will get you far. For extremely fast performance (CPU and GPU) take a look at https://neanderthal.uncomplicate.org/

11☝️
dorab 2020-12-19T17:01:50.012500Z

None are really beginner topics, but (as you said) good to know they exist.

2020-12-19T18:29:21.013700Z

Thanks a lot! Will take a look and back out when my head starts spinning.

2020-12-19T18:45:30.014200Z

The normal Clojure vectors you get by default contain all boxed values, but there are also Clojure vectors restricted to one JVM primitive type, e.g. (vector-of :long 1 2 3) that are more memory efficient, and I would guess are able to get one closer to the speed of iterating through Java arrays of restricted types, but I haven't compared performance between those two approaches.

1😮
alexmiller 2020-12-19T18:50:26.014800Z

it's undocumented because that's not a thing

2020-12-19T18:51:58.015200Z

Is it one of those cljs things?

alexmiller 2020-12-19T18:52:10.015400Z

oh, could be, don't know

alexmiller 2020-12-19T18:52:28.015800Z

you can extend protocols to Object or nil to cover default cases for those in Clojure

1👍
2020-12-19T18:56:10.020Z

Cljs in a few places supports variations of 'default' or ':default' for types because of the type hierarchy in js

clyfe 2020-12-19T18:56:51.020900Z

It's an old article of David Nolen https://archive.org/details/hackermonthly-issue023/hackermonthly-issue023-ipad/page/n31/mode/2up

clyfe 2020-12-19T18:57:00.021300Z

Sol yeah, probably a cljs thing then

alexmiller 2020-12-19T18:57:31.022300Z

might be worth posting on https://ask.clojure.org as a request for doc improvement (or https://github.com/clojure/clojurescript-site/issues for web site doc)

2020-12-19T18:57:37.022700Z

Definitely cljs

alexmiller 2020-12-19T18:57:57.023200Z

not sure which or both are most useful

dpsutton 2020-12-19T18:58:13.023800Z

It’s in the doc string of extend type in cljs it seems

dpsutton 2020-12-19T19:00:56.024300Z

And it’s quite clear in its wording

clyfe 2020-12-19T19:03:25.024600Z

ty all, apologies for the wave

dpsutton 2020-12-19T19:04:41.024900Z

no worries. i'm glad i looked. had forgotten about that

dpsutton 2020-12-19T19:05:11.025600Z

and i didn't mean to sound like "it was a dumb question because its right here" but more "its a great question and i didn't know and here's how i answered it"

2❤️
clyfe 2020-12-19T19:46:38.026300Z

> You can implement a protocol on nil > To define a default implementation of protocol (for other than nil) just use Object

clyfe 2020-12-19T19:46:48.026500Z

Seems it's already on the website.

Young-il Choo 2020-12-19T20:57:34.026900Z

To define macros, you need to understand the difference between • quote (’ apostrophe) • syntax-quote (` back-tick) • unquote (~ tilde) The syntax quote needs to be used, when you need to create an expression using a variable’s value, not the variable itself, done by unquoting the variable. In your example you need to syntax quote the (prn …) and unquote message. Also, you need to quote nil, since that is a return expression.