Fork me on GitHub
#clojure-europe
<
2020-10-29
>
RAMart07:10:45

☀️

raymcdermott08:10:07

I’m in a reflective mood today, good morning

borkdude15:10:23

There's a Clojure thread on Hacker news front-page now :)

otfrom16:10:48

as long as comp.lang.lisp stays away

borkdude16:10:35

I can handle them 💪

otfrom16:10:27

I should stop being so mean

otfrom16:10:43

I blame too many years trying to learn lisp from comp.lang.lisp

borkdude16:10:06

Oh, I know what you mean, I was there in the early 2000s learning Common Lisp...

borkdude16:10:16

Maybe I was that guy who offended you?

otfrom16:10:07

ooh... maybe

otfrom16:10:38

I don't think so tho. I remember how offended everyone in c.l.l was about a lisp on the jvm that betrayed the lisp machine ideals

otfrom16:10:53

it was one of the happiest days of my programming career

otfrom16:10:10

a good lisp on the jvm with good interop so that I could use the java libs I already knew? Fantastic.

borkdude16:10:54

For me Clojure was a way to learn about the JVM. I was only familiar with .NET and didn't like what I saw comparing the built-in date library and some more.

borkdude16:10:18

But I needed to work with the JVM because I was a lecturer back then and these students were mostly taught Java

borkdude16:10:18

Within a year or two I was running a Clojure course to teach them some next level programming :P

borkdude16:10:34

I was lucky to avoid teaching OOP design patterns because I couldn't comfortably teach that while knowing the alternatives

borkdude16:10:06

I mean, I would act neutral, but some things you can't easily hide

borkdude16:10:37

I like it as an intellectual thing, but I don't like it from the perspective of writing boilerplate in a language you have to work towards freedom first

otfrom16:10:30

oh gods, the way people abused it was terrible, but actually understanding the patterns made it easier to think your way around a problem without having to invent everything yourself

otfrom16:10:36

I really liked builder

otfrom16:10:51

the clojure data structures seem to use the flyweight pattern

borkdude16:10:07

yeah, you could apply some of those patterns in Clojure as well, but most of it is just plain common sense don't you think

otfrom16:10:08

I think they are often common sense, but that is why programmers would never know them 😉

otfrom16:10:23

I don't think most of them make sense in fp languages

otfrom16:10:29

they are very OOP

otfrom16:10:05

but for implementing fp languages in OOP languages (like clojure in java) I think they are probably pretty useful, just don't see them as things you can copy and paste from

borkdude16:10:06

yeah, maybe put it too strongly, but these books of hundreds of pages of material which would almost be trivial in Lisp/FP, it didn't sit well with me

otfrom16:10:07

fair enough, but that is a problem with OOP not patterns

borkdude16:10:24

yes, I was referring to OOP design patterns specifically

otfrom16:10:06

I think with Language of the System that RH is reaching for some FP patterns

borkdude17:10:39

Now that I'm ranting anyway: I wish core wouldn't have used the 2-arity of map, filter etc for transducer. I built a clj-kondo type system for it to reduce the amounts of errors I get because of mistakes I make with this.

Ben Hammond18:10:08

the whole transduce thing of • arity 0 for initialise • arity 1 for finish • arity 2 for iteration seems like a really strange design choice to me

Ben Hammond18:10:28

it would certainly be clearer to declare as a record

Ben Hammond18:10:19

the reason I can think of is that JVM is massively optimised for overloaded functions, and this was felt to outweight the confusing code implications?

Ben Hammond18:10:43

Is there are more compelling reason that I have missed?

Ben Hammond18:10:11

(Er is that what you were talking about...? Or I have missed the point?)

borkdude18:10:00

My gripe was with (map inc) vs (map inc [1 2 3])

borkdude18:10:47

I would have preferred (mapping inc) as forgetting to pass args to (map inc) is often just a mistake in my code

borkdude17:10:12

I had another one right now: there was a JSON payload that silently missed a field, turns out it was because a transducer could not be serialized, yeah, duh...\

borkdude17:10:35

I would have preferred (mapping inc), (filtering odd?) over (map inc) and (filter inc) any time of day

otfrom18:10:47

morning Jade

otfrom18:10:34

@borkdude thx for clj-kondo. It is making my refactoring to get around not being able to debug things a lot easier. Couldn't get through this w/o your tool.

otfrom18:10:50

(I'm basically coding by squiggly lines atm)

slipset20:10:55

@borkdude just got my emacs to talk to clj-kondo. Lovin’ it!

slipset20:10:14

Here’s a couple of linters I’d like (but they’d certainly be controversial): 1. Prefer partial over #(..) when all you’r doing is binding variables 2. Prefer named (fn foo […] …) over #(…) and certainly over (fn […]…) 3. Flag #(..) or (fn foo […] …) spanning multiple forms/lines

slipset20:10:16

I guess 1) is hard, since you have to analyze the #(..) to understand that you’re just “currying”. 2) would be simple enough, warn/error on #(...) or (fn [..] ..) 3) should probably be doable…

borkdude20:10:14

I wouldn't know why one would prefer partial over #(..) or (fn [...]), seems a bit random to me + you will lose reloading for vars you bind over with partial

borkdude20:10:42

3 seems a bit arbitrary to me. 2. could implement, but not sure anyone would adopt this as a linter and adhere to it strictly in their code base. As an example, I recently implemented the shadowed-var linter. In theory very useful, but after using it a bunch, I found it too restrictive myself

slipset20:10:51

It might be a principle somewhere around it, but it’s basically that when I see partial I know that there is nothing more going on than the binding of variables. If I see #(..) I have to read the form to se e that nothing else is going on.

slipset20:10:15

I’m not at all asking you to implement, but it might be fun for me to try to implement at some point.

slipset20:10:58

And I could deffo see that those are not linters that everybody would use.

borkdude20:10:43

implementing is one thing, maintaining is another one. if something is wrong with a linter, usually I'm the one ending up fixing it, even if some other person wrote it (unfortunately). my point was more like: what's the point of implementing a linter and maintaining it indefinitely if it's too strict for anyone to use

slipset20:10:04

As for 3) we have examples in our codebase of

(reduce (fn [acc x] 
 ;; 300 lines of code
  ) {} lol)

slipset20:10:26

Hmm, didn’t consider the maintenance part, and that’s a fair point.

borkdude20:10:27

ah good example, I get the point

slipset20:10:11

Someone asked for “linter plugins” in Eastwood at some point.

borkdude20:10:44

clj-kondo has this, it's called hooks. you can use it today. docs: https://github.com/borkdude/clj-kondo/blob/master/doc/hooks.md examples: https://github.com/clj-kondo/config

slipset20:10:24

Right, so I can basically figure out how to write my own linter, stick it in a hook and voila presto?

borkdude20:10:38

hopefully, yes

borkdude21:10:07

what you can do with a hook is transform the shape of a call and do some linting on the original shape of the call

borkdude21:10:13

so it's a kind of macroexpansion

borkdude21:10:26

but then using clj-kondo / rewrite-clj node primitives

slipset21:10:03

The thing with partial is somewhat related a thing I first heard on the Idealcast with Gene Kim and Michael Nygaard. Then I heard it again on an episode of the Corecursive podcast.

slipset21:10:48

The thing in the podcasts was something like “The more abstract the types of the parameters of the function, the less the function can do”

slipset21:10:11

So if you have a fn signature fn T t => T (sorry about the syntax) where T is some type, then this fn can only do one thing.

borkdude21:10:41

in Haskell yes, in Clojure we don't have this

slipset21:10:48

if you have a fn signature fn Int i => Int you have a function that can do an infinite amount of things

slipset21:10:05

So if you have (map (partial ...) foos) you know that you’re only binding parameters, whereas if you have (map #(….) foos) anything can happen.

borkdude21:10:43

I don't really see the improvement. (partial foobar 3) or #(foobar 3 %) convey the same information and don't guarantee anything about potential side effects

borkdude21:10:42

partial returns a closure. e.g. (partial foobar x) will capture x at one point in time. any changes to x won't be seen in that closure. which prohibits REPL-driven development.

slipset21:10:06

My point is that the moment I see partial I can almost ignore the rest, but I have to read #(…) to the end to see that there is nothing else going on.

slipset21:10:18

Right, didn’t consider that one.

otfrom21:10:30

I didn't know about not seeing the redef of foo

borkdude21:10:33

I'm not saying: always use this or that, but a linter will say that and this is where I see not many people using it in the long run

otfrom21:10:36

that is a sneaky gotcha

slipset21:10:47

I tend to do C-c C-k in emacs which solves the redef of foo

otfrom21:10:04

I do that too (so hopefully that works)

borkdude21:10:15

not if foo is from another namespace, unless you're into the entire REPL reloaded workflow

6
otfrom21:10:30

I only really do C-x C-e when I'm doing stuff in rich comment blocks

otfrom21:10:39

anyway, good night all 😉

slipset21:10:07

Is the grownup telling us to go to bed?

slipset21:10:24

Sounds like good advice.

dominicm22:10:35

I don't like partial because it obscures the arguments expected by the new function you've created. I've ended up in a muddle because of that many times.

👍 9
3
Ben Hammond08:10:59

when you choose partial you are signalling that you expect at least two different arities, and that is a feature, seems to me