Takes a vector of function specs and a body, and generates a set of
bindings of functions to their names. All of the names are available
in all of the definitions of the functions, as well as the body.”
It seems that the official clojure documentation makes a lot of sense to those that already know what a function does.@socksy: is that letfn? i'm just guessing.
yep
had to look for an example, then the explanation made perfect sense
but really, this is not a good way of writing documentation
what would you weite instead?
(and i think i’m now against letfn for 95% of the time, and shall be eradicating its usage throughout our codebase)
i also don't really use it - every time i do, i end up with a normal let or even just put functions in vars
I’m not sure how I’d write it. Something like “ A form of let binding specifically for creating functions in the let scope. Inside of the binding vector you have [(fn-name [args] (body))], which you can then call as (fn-name) inside of the binding as (fn-name args). Each fn in the binding vector can access each other.” But really, good documentation requires a lot of thought, and that probably can be improved a lot. Also depends on the let documentation.
but yeah, the reason i’m against it in our code base is that it’s been used as a top level construct, with only one function binding. Totally unnecessary and harder to read than just an extra private function
i think the term "recursive" would clarify things a bit. letfn is useful for (mutually) recursive local functions, no?
yes, but it’s not a prereq
not a prereq, but why would you use letfn if you don't need the recursive aspect of it?
(but to me it seems like one of the only acceptable use cases)
i mean, it’s not actually recursive, it just lets you refer to any other function inside of the let binding, hence letting you do mutually recursive things
it is also the only way to define a recursive local function.
so i take letfn as an indicator for upcoming recursion, and let is clearly defining only non-recursive functions. the documentation would do no harm pointing out that letfn is only needed for recursive local functions.
you’re right. Makes sense
never occurred to me that clojure’s one pass “can’t refer to symbols not yet defined in this file” also stops you from doing mutually recursive functions. Kinda shitty
It does not really stop you. If you need mutually recursive global functions, use declare
.
tbf, the web page documentation for letfn has a mutually recursive fn as its example. It’s just the doc-string I’m taking exception at
good to know, cheers
tbh I think the docstring is quite clear in explaining what letfn
does. It does not cover why you would want to use it, for which use cases. But this seems to apply to almost all docstrings, doesn't it? And that is probably a good idea, to avoid bloating the docstrings and quite abstract functions are hard to describe with respect to their use cases. @socksy
My issue is that it’s only clear when you already know what it does
which you can argue as the reason for the doc string, just to check which arguments it has, but really the function signature should be clear enough in that respect
hm, I dare to disagree. The name alone explains a lot - assuming one knows let
already - and then the only missing piece is, that the functions are available not only in the body (or consecutive functions) but in ALL of the functions.
I know what a let binding is, I know what functions are, and yet from that docstring i couldn’t work out what letfn did from the docstring, which is possibly a testament to my stupidity, but this really isn’t an abstract function
When I read the docstring - first time a few minutes ago - I could rather easily understand what it does. However, without reading the follow up discussion I did not think "Hey, awesome, I am going to use that for mutually recursive, local functions"
and you missed a subtlety I think. It’s not like a let fn, since the binding vector is perfectly allowed to have an odd number of forms
uh, indeed, I wasn't aware of that. and cannot get it from the docstring alone. need to investigate a bit more! :simple_smile:
(letfn [(foo [args] (print args))] (foo bar))
ah right. well I am now thinking about the syntax of the letfn
special form, specifically the functions specs. That's a bit unfortunate I would say. But as every function spec has enclosing parenthesis, it's of course quite obvious, that there can be odd number of bindings
but nevertheless, I struggled more than once with other docstrings