Fork me on GitHub
#clojure-uk
<
2020-08-12
>
dharrigan07:08:20

Good Morning!

jiriknesl07:08:35

Good morning

dominicm08:08:59

Morning 🌄

dharrigan08:08:27

I've started to see a bit more letfn in my travels around the clojure codespace. Do people find them useful?

rickmoynihan08:08:11

I use them from time to time; though often you end up needing a let anyway if you need more non function bindings. They can be good for implementing localised statemachines that close over some common bindings.

rickmoynihan08:08:02

Unlike def they support forward declaration, so they’re good at handling mutual recursion etc e.g. forward declaration:

(letfn [(a [] (println :a) (b)) (b [] (println :b))] (a))

rickmoynihan08:08:39

mutual recursion makes them quite good for parsers etc

dharrigan08:08:01

I see, so in a you're forward referencing b even although it's not defined just yet.

dominicm08:08:45

Not really. I rarely see them.

jakob.durstberger08:08:58

Good Morning ☀️ 🔥 💀

alex.lynham08:08:25

I don't think I've ever used a letfn in production code

dharrigan08:08:58

Would it be a considered a code smell? (apart from the usecase referenced by Rick)?

alex.lynham08:08:31

idk I tend to consider anything that's not super primitive a code smell haha

alex.lynham08:08:01

i try to write code that i can debug while tired, under stress cos prod is down

rickmoynihan09:08:06

Even though a let in that case is arguably a little better I personally wouldn’t really consider it a code smell if it was a standalone letfn it’s something I’d probably only change if you ended up needing to add new bindings that would mean you had something like (let [bar ,,,] (letfn [(foo [] ,,,)],,,)) in which case I’d wait till that point before refactoring it into (let [foo (fn [] ,,,) bar ,,,] ,,,)

rickmoynihan09:08:38

Just checked and https://github.com/bbatsov/clojure-style-guide doesn’t mention letfn (not even in issues) so it seems in eight+ years nobody has thought using it is particularly smelly

rickmoynihan09:08:24

clj-kondo also doesn’t seem to lint it as a potential smell either; though it does check it’s used properly… clj-kondo could easily check it was only used for forward declaration or mutually recursive uses; so if it’s something you care about, you could possibly open an issue on that repo for discussion.

rickmoynihan09:08:01

I think the main argument against using it, is that it’s less familiar to people than let and let is more general; though some may consider the extra specificity of it as a benefit

rickmoynihan09:08:05

Just scanned one of our larger code bases for uses of it, and it’s currently used in two places… none of which use it for forward declaration or mutual recursion…. both cases define it immediately at the top of the function definition to define a localised helper function that closes over some of the outer arguments; and that helper is repeatedly used multiple times within the body. Personally I think this can be a good use of it, as the letfn makes it very clear that the helper is local where as merging it into the let it wraps would hide the helpers important role.

alex.lynham09:08:19

I tend to just define the helper at the top of a regular let since there's usually other bindings

alex.lynham09:08:36

tho i'm sure you can find examples in code where i totally break that rule

borkdude10:08:08

I would use letfn for mutually recursive functions, not for anything else

rickmoynihan11:08:48

would you consider adding a lint for it to clj-kondo?

borkdude11:08:54

well, it's not bad to use it, just unnecessary. I think a warning might be a bit too much

rickmoynihan11:08:22

Yeah that’s my feeling too

rickmoynihan11:08:50

it’s not particularly hard to read; and it’s not very commonly used anyway

dharrigan11:08:09

Thanks everyone! A very informative disscussion!

alex.lynham11:08:32

possibly sacrilige here but I'm enjoying peering into purescript JS dev

alex.lynham11:08:39

halogen is p like re-frame

alex.lynham11:08:51

you can really see the ML <-> lisp connection in the DSL

jakob.durstberger14:08:07

I have looked into Haskell and Purescript with Halogen a lot last year and in the end I found myself fighting against the typechecker a lot. I was really excited about this whole idea of explicit side effects until I tried to build actual software 😄

alex.lynham15:08:23

yeah that's largely been my experience too

alex.lynham15:08:42

but although I love re-frame I've often found cljs pretty difficult to love (although shadow has massively improved that tbf) and types in node land make a fair bit of sense, as well as being able to do monadic stuff around asyncs and whatnot

zyxmndaleyjes19:08:50

Bro it's 5.30pm

thomas19:08:52

We run UGT here, so morning is perfectly fine

zyxmndaleyjes19:08:53

But I think he's in Cambridge 😂

thomas19:08:07

that is the beauty of UGT, it doesn't matter where you are.

zyxmndaleyjes19:08:21

Oh I wasn't aware of Universal Greeting time

thomas19:08:23

nor where anyone else is

zyxmndaleyjes19:08:30

Good morning then

thomas19:08:08

that is ok.... instead of having the conversation of morning... no here where I am it is not morning we just discuss UGT instead

folcon22:08:09

Yep, that's pretty much how it works 😃... @ ;)...