Fork me on GitHub
#clojure
<
2018-05-12
>
lloydshark06:05:51

Howdy all. I did a 1 hour clojure introduction talk at my work this week. This is what I came up with. https://github.com/lloydshark/clojure-intro/blob/master/src/clojure_intro/part1.clj

matan08:05:16

Hi Y'all, I was scheduled to talk about Clojure for Scala application developers.

matan08:05:13

Problem being, after writing a Scala project very recently, I'm not sure what the benefits of clojure might be except the following things that may seem benign coming out of a month of Scala:

matan08:05:32

• not having to wrap functions in objects in order to compose across an application

matan08:05:17

• a proper macro system as opposed to Scala's somewhat hackish one (but the crowd will push back on this)

waffletower04:05:38

A non-lisp defending its macro system against the macro system of a lisp has quite a battle ahead of it

matan08:05:18

• leaner syntax ― not everyone find it a benefit, kind of a personality preference

sobel12:05:19

I encourage you to flesh this point out a bit. The syntax's lack of operators, flow control, and creation patterns (those are all function calls) makes it really clean for transformation by macros, clean to compile, etc. Lean syntax is not just less typing, it's a less-is-more situation. Focus on what sexprs enable!

matan08:05:29

clean to compile?!

matan08:05:44

• transducers are nice, however their usage syntax IMO suffers a little from history as they weren't built in from the get go. Maybe I'd say the same about channels, not sure though.

matan08:05:30

• clojure has nice local concurrency idioms (well concurrency is local by definition), but Scala has Akka, so kind of a trade-off

matan08:05:13

Essentially, both languages are immutable-first, and functional, and JVM friendly. Scala is accomplished in large projects, sort of.

rodrigojuarez08:05:32

has anyone worked with audio libraries in Clojure? I want some library to do audio visualisation, I just need to process the audio though, the display I’m already doing it with Quil

soulflyer10:05:20

@rodrigo.juarez.inf Overtone handles samples. It might be done in supercollider though so I'm not sure how much use it will be. https://github.com/overtone/overtone

rodrigojuarez10:05:13

Perfect @soulflyer I’ll check it out

leonoel13:05:25

@matan I would add : * clojure is not an experimental language, each new version coming up with zero breaking change since the first production release * clojure is data oriented, designed from the ground up to work with reified attributes and records as collections, which fits business concerns much better than static containers

manutter5114:05:01

@matan my two cents worth is that it's a lot easier in Scala to write procedural code around mutable state, which is the easy road to programming hell. Clojure's strong bias towards functional programming is a huge benefit because not only will your code be more functional-oriented, but also the libs and dependencies you use will be functionally-oriented. Others may or may not agree, but that seems like a substantial advantage to me.

matan14:05:38

@manutter51 Well that's a bit in the category of "helping to save you from yourself", as both languages are functional to begin with... or is there much more to where your thoughts on this started from @leonoel could you kindly elaborate on the point of reified attributes?! (and also records as collections)

leonoel14:05:45

basically, what Rich is talking about in the second part of his latest talk https://youtu.be/2V1FtfBDsLU?t=2267

leonoel14:05:35

in a typical business scala app (like in java), the code is cluttered with mappings of attributes from case classes that are almost the same except a couple of fields less or more. the reason is because attributes are not reified, there is no way for the compiler to figure out that a field in a class actually refers to the same attribute as another field in another class.

matan15:05:03

@leonoel thanks! is that video touching specifically on those exact points? I'm kind of weak in thinking through the lens of reification

leonoel15:05:02

yes. reification in this context just means "available as a value at runtime". keywords are values, class fields are not. functions are values, methods are not.

matan15:05:44

@leonoel I'll have to figure some good crisp examples for myself, I probably lack in some area of knowledge that would otherwise allow fully grasping it in the abstract

matan15:05:41

I'll probably need to take a good clojure example really demonstrating this, and as a thought drill try to see how hard it would be accomplishing the same result in e.g. Scala

leonoel16:05:51

for example, you need to return all information you know about an entity. this information is scattered in several parts of your system, you need to perform multiple calls in parallel and merge results. in clojure the final operation is just a merge, in scala you have to make a new type and implement the conversion

leonoel16:05:41

another example. you made a function using a subset of the attributes of a given entity to produce a value. in clojure you can pass any map having the given subset of keys, in scala you have to perform the conversion to the subtype by hand

leonoel16:05:07

(this is the "UPS truck" example from the talk)

leonoel16:05:15

at some point some scala devs just give up and make a huge case class with all known attributes of a given entity wrapped in Options, and suddenly all the benefits of static typing disappear

leonoel16:05:35

and this doesn't even solve the merge problem

matan11:05:05

oh thanks, just saw this now

leonoel14:05:34

in clojure, attributes are values (keywords) and records are collections (maps), you can iterate on attributes, you can merge two maps, you can make arbitrary joins

leonoel14:05:27

all of that is incredibly hard to do with static containers

andre.stylianos15:05:14

@matan you're missing one of the most important benefits, the REPL.

manutter5115:05:48

@matan The advantage I see is that Scala is both functional and procedural, which means as soon as you start including dependencies, you're getting code that could be either functional or procedural. I've only dabbled in Scala, so take my opinion for what it's worth, but it seems to me that there's an advantage to Clojure's strong bias towards functional-only code, in that you're a lot more likely to find your dependencies are also functional-oriented. It's not just saving you from yourself, it's saving you from other people's procedural code down in dependencies that you might not even know you're pulling in.

manutter5115:05:31

And of course, Scala's not a bad language, so it's more of a personal/philosophical preference, but I mention it as something that might appeal to some people.

matan15:05:38

@manutter51 actually, nobody is writing procedural code in Scala... other than performance critical bits (where you should write a Java class, same as in clojure, or go mutable, for, say, implementing a chart/dynamic programming algorithm), writing procedural code is not idiomatic in Scala... I'd say these differences are kind of benign, as you can shoot yourself in the foot in many ways either way

matan15:05:42

@andre.stylianos Scala has a REPL, of course, and had that from day one more or less

matan15:05:47

If I may say so, I tend to think the points from @leonoel might be key, but let me admit I'd need to see an example and not just a (typically) midway abstract talk from the Rich H.... 😉 I will try to use my imagination till I see one in context

manutter5115:05:49

That's interesting, I didn't know that. I have to say, the most time I've spent with Scala was doing the "Functional Programming in Scala" course on Coursera, and that was pretty fun. It was interesting enough that I bought a bunch of eBooks on how to program in Scala. But Clojure's my first love -- it's so concise and elegant, and it really matches my thought processes.

andre.stylianos15:05:58

Can you interact with the running program with the Scala REPL? Do changes to it without losing the application state?

matan15:05:05

@andre.stylianos I fail to see any difference in REPL prowess between the two. But just to be sure, about interacting with the running program, how do you mean that?

matan15:05:19

@manutter51 that seminal course was actually quite horrible in retrospect, and if it left you thinking idiomatic Scala is not functional (aside mixing with OO) it kind of stresses that point to me 🙂

matan15:05:40

Don't get me wrong, clojure has made me a better programmer, (albeit, not more productive all-in-all))

andre.stylianos15:05:06

I'm currently on my phone, will answer in a moment. By the way, I also do like Scala, I used it in my previous job, and I agree that in general Scala code is no less functional than clojure

tbaldridge15:05:54

@matan Scala has a interactive shell, not a REPL

tbaldridge15:05:15

A key part of REPL is having the READ, EVAL, PRINT parts. What's the reader in Scala?

tbaldridge15:05:07

What are the functions involved in this code translated to Scala? (-> "(+ 1 2)" read-string eval println)

manutter5116:05:38

@matan Really, you thought it was horrible? I'm surprised. I had a great time. I just always heard Scala evangelists pointing out that Scala lets you do both functional style AND imperative style, as if that were a major selling point, so I got the impression that was seen as a useful feature of the language. If it's not idiomatic to use the imperative style, then that was a false impression.

👍 4
sobel16:05:05

every time i can insert "p" before map, and tell with a glance that my code is reentrant-safe, i am glad i coded in clojure. i am not aware of another JVM language that offers anything like that.

matan16:05:03

I think writing a scala pmap is 15 minutes of work though

the2bears16:05:05

@matan my experience with the Scala code I've seen in the workplace is that it's basically Java with the Scala syntax. These were Java developers who were not taking advantage of the functional language features available to them. You might call this "helping to save you from yourself", but it's a very real thing 🙂 So what you ended up with is messier code. Actually, that's my overall feeling for Scala. I had a role that was advertised as a Clojure one, only to have it changed to "pull apart the Clojure and rewrite using Scala and Akka". If you use the functional side of Scala, it's okay, but man... just felt messy to me.

WhoNeedszZz14:05:59

This has been my experience in seeing enterprise Scala code as well. Dealing with it was a nightmare.

andre.stylianos16:05:12

@matan I was never a heavy user of the Scala REPL, leaning more on the type checker while I was developing, but as far as I know it is nowhere near as powerful as the Clojure REPL. With the Scala one you can spin up a REPL, and interact with code, do some exploration but that’s it. It is a standalone and does not communicate at all with your application. With Clojure you can actually use the REPL as you are developing to evaluate code directly in the context of your running application and make changes to that same application with no need to restart it. If you have a Scala server running, can you just write a function that queries your database and just send it through the REPL so that it is evaluated in the context of your application? Or rewrite a function and evaluate it so that it is available for your application to use? Or change the connection parameters for your database on the fly? In general with Scala you can write small experiments that later you copy over to be used in your actual codebase, but with Clojure you can actually develop you whole application mostly using the REPL by evaluating code directly in the context of your application.

andre.stylianos16:05:24

In regards to syntax, general complexity and breaking changes. Even Odersky admits that some parts of Scala can be quite complex, and the result of that is the new Dotty compiler which as far as I’m aware will result on simpler syntax and type inference rules but with it also comes some breaking changes.

matan16:05:29

@andre.stylianos I've never used those capabilities of the clojure REPL actually, which may come to explain my lack of improved productivity. I have to learn or practice how to do that. I've also found it annoying building my app through a black&white REPL that doesn't shine with multi-line expressions, v.s. a syntax highlighted editor environment.

andre.stylianos16:05:54

All of emacs, vim, Atom, and Cursive(Intellij IDEA based) allow you to just send code to the REPL directly from your editor.

andre.stylianos16:05:10

This talk presents a bit the things I mentioned

sundarj16:05:59

there's also this talk https://vimeo.com/223309989

8
matan16:05:21

Thanks, I guess I didn't go that path as I typically used clojure for data processing applications, not server applications. Will sure study that video from that position!

andre.stylianos16:05:08

It takes a bit of a shift in mentality to start working like that, coming from a code, compile, reload cycle but it really is eye opening after you get it.

👆 4
andre.stylianos16:05:06

I just used server applications as an example, this is a good workflow for Clojure development in general. You can even do it with ClojureScript in the browser.

matan16:05:26

As an aside, are these conversations here on slack ephemeral, or are they archived for future use?

sundarj16:05:54

it's a bit behind atm, but there are logs at https://clojurians-log.clojureverse.org

sveri17:05:35

@matan Take the time to learn to use the REPL properly combined with your editor. From all the languages I tried clojure gives the best REPL experience in terms of connection to your application. Its one of the main points that always pulls me back whenever I try something else.

matan17:05:45

@sveri will do exactly that on my next application! using Atom BTW, for all languages I code. For clojure, in conjunction with proto-repl of course...

👍 8
the2bears17:05:37

@matan I gave a Clojure "lunch and learn" at a previous job one time. Showed a game I was making as part of a demo, and used the REPL to make live changes in the running game. Was definitely effective as a demonstration of what was possible in the programming style. At another job, we would query REPLs in running test environments, get the git commit used in the running build, check that out locally and fix a bug. Revert, and if the fix worked, you have an easy path to commit and re-test.

matan17:05:17

BTW that's oftentimes quite tricky: > get the git commit used in the running build I find myself embedding an external process run of git hash in the logs for that myself, otherwise, quite non-deterministic 🙂

the2bears18:05:59

I can't think of the plug-in name at the moment, but there's one for lein that we use. But you're right in that it can be tricky - process and discipline! 🙂 But this way we have release builds generated from git, and we can exactly determine what is running. You then need to be sure you revert and re-evaluate any functions you changed. But the benefits of adding logging on the fly, or binding arguments to a function then inspecting them saved us a lot of time on many occasions.

matan17:05:30

@the2bears thanks for sharing! very helpful

the2bears17:05:02

NP, what I meant to add was that we were connected to the REPLs and evaluating code in them all while doing this.

4
matan17:05:04

Okay guys, so REPL driven development.

matan17:05:39

How does one tangibly crystalize the abstract benefits of reified attributes and data centricity, the core design setting apart from Scala I'd say, is something I'll keep working towards. Any pointers to inspiring examples would be ever so welcome

andy.fingerhut18:05:46

@matan They aren't necessarily concrete examples of particular programs, if that is what you are looking for, but some of Rich Hickey's talks have mid to high level pros and cons of data vs. objects, and there are transcripts for many of them linked from the videos and vice versa. Here is a list of transcript links: https://github.com/matthiasn/talk-transcripts/tree/master/Hickey_Rich In particular, "Effective Program", "clojure.spec" (for why objects as data containers vs. maps/dicts often lead to unnecessary proliferation of object types), "The Value of Values".

andy.fingerhut18:05:26

Sorry for the noise if that is exactly what you were not looking for 🙂

sobel18:05:07

There's a folk-wisdom I hear, "it's better to have 10 functions on one datatype than 1 function on each of 10 datatypes." That hints at what you might want to go into: composition is enabled by having functions that operate on the same datatypes

joelsanchez18:05:26

afaik that quote is from Alan Perlis, and it's usually quoted as: "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures."

matan19:05:52

Anyways, on its own, this is kind of merely a slogan for those who already saw the light :snow_cloud:

sobel20:05:44

I figured I would get my ratios off 😉

sobel20:05:42

And I agree, it's a slogan for those in the know. I was suggesting you might want to illuminate on the topic.

matan11:05:20

Honestly I still haven't seen the light 🙂 and that's after many lines of clojure code. I know how to design object oriented, and I know how to compose in clojure and even enjoy it. Can't see why one is better than the other, I've probably not coded to an edge case where this really makes a difference whether I have 10 on 10 v.s. 100 on 1.

matan11:05:27

I guess call me shallow 😉

sundarj16:05:58

@matan in Clojure, all of the sequence functions, map, filter, etc work on a single (abstract) data type: seqs. that's the '100 functions on 1 data type'. this means you can use the same functions no matter the situation, so long as you can get a seq to work with (which all these functions do by calling seq internally), and you never need to worry about specific data types breaking the code you're writing - you can just use whichever function suits the task best. in JavaScript (for instance), map and filter are Array methods, and thus only work on Arrays. there are other data types, like Sets and Maps, but since map and filter are Array-specific methods, you cannot use them with the others, and vice versa; you cannot use Map's keys method with Arrays - you have to re-implement it separately (or somehow turn an Array into a Map). this is the '10 functions on 10 data types'; you have to worry about which specific data type you're working with in order to know which methods you can use on it. you lose the ability to write code without caring about the specific data type you happen to be working with; you lose the ability to write generic code (without a bunch of if statements at least).

sundarj17:05:41

incidentally, that quote is from Alan Perlis' Epigrams, and there are loads of them: http://www.cs.yale.edu/homes/perlis-alan/quotes.html

matan08:05:05

Hey thanks. Any reason you juxtapose with JavaScript and not Java above?

sundarj14:05:58

i just went with what i know 🙂

sobel18:05:19

I find Clojure's simple data types so powerful that I get myself into a little trouble sometimes. That's appropriate calibration for my professional use. My tools should be just dangerous enough.

sobel18:05:10

Compare to strongly typed OO for modeling, and it's too safe. I can't compose with objects.

matan19:05:21

I guess I'm still looking for that pristine program demonstrating these mid to high level ideas in a very tangible manner. Has anyone possibly bloged about this very methodically?

sobel20:05:02

What would make it tangible to you?

sobel20:05:24

I mean, that's a curious word choice for an entirely-abstract line of work 😉

andy.fingerhut21:05:02

For documentation writing purposes, I am looking for a name to call the category of objects that include futures, delays, and promises, if there is even a single name they can all reasonably be called. Vars, refs, atoms, and agents I have seen all called mutable objects, or mutable references, in Clojure, but futures, delays, and promises are not really mutable, since they have a value assigned to them at most once.

sundarj22:05:03

futures, delays, promises, and lazy seqs all seem to implement clojure.lang.IPending, which is used in realized?, so perhaps IPending or 'realizable'?

andy.fingerhut22:05:18

Those sounds like good candidates.

andy.fingerhut22:05:20

I chose 'pending'. Caveat: lazy-seq's are also IPending, too, although I didn't want to include them in this category for my purposes, so I went ahead and enumerated the list of things I did want to include, too, via (

andy.fingerhut22:05:29

i.e. futures, delays, promises

sundarj22:05:39

fair enough 🙂

mg00:05:22

Deferred computations maybe