That example is as point free as it gets. Just because the syntax doesn’t look like Haskell doesn’t really change that.
You are applying functions to arguments, aren't you? So "point-free" means you cannot apply a function to arguments on the left hand side of a definition, but you are allowed to do so on the right hand side? If that's point-free, it is also point-less.
tacit means a definition doesn't name it's arguments, not that it isn't applied to arguments. the word tacit means implied, so arguments are implicity worked upon, instead of explicitly.
Why not translate your code to pointfree style automatically? Using[0], you can go from quad a b c = let d = b * b - 4 * a * c in ((-b + sqrt d) / 2 * a, (-b - sqrt d) / 2 * a) to ghci> import Control.Monad ghci> quad = ap (ap . ((.) .) . ap (ap . (liftM2 (,) .) . flip (flip . ((*) .) . flip flip 2 . ((/) .) . (. sqrt) . (+) . negate)) (flip (flip . ((*) .) . flip flip 2 . ((/) .) . (. sqrt) . (-) . negate))) (flip (…
Apart from "just because we can" or "it's fun", why on earth would someone prefer the second style?
tacit programming means you don't use argument names to direct your data to the desired output. what's interesting to me about that is the unexplored possibilities of how data could be directed without names.
Tinkering with APL (Dyalog) gave me one of my most mind-bending programming moments. dismal ← 10⊥(⌈/10⊥⍣¯1⊢) This is the complete solution to addition in the framework of Dismal Arithmetic [1]. The pivotal idea there was the inverse of a function, and "trains". Until that moment of insight, I was fiddling about with dfns, which looks janky in comparison. dismal ← {10(⊤⍣¯1)⍵}∘{⌈/⍵}∘{10(⊥⍣¯1)⍵}⊢ ⍣¯1 is APL for "inverse…
> dismal ← 10⊥(⌈/10⊥⍣¯1⊢) And what does that look like in C?
In traditional C style, something like
unsigned dismal(unsigned x, unsigned y) {
unsigned z;
for (z = 0; x || y; x /= 10, y /= 10)
z = z * 10 + (x % 10 > y % 10 ? x % 10 : y % 10);
return z;
}
This led to the algebra of programming school: https://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/s... For context, see this discussion: https://stackoverflow.com/questions/5671271/what-are-advanta...
Absolutely every single time when I use functional programming, I store my intermediary calls in a variable, specifically because naming that variable forces me to explain what that intermediary result should be. If the intermediary result makes no sense, and only the function composition makes sense, I'll create a new well named function that does the chaining, even if it's single use. This is literally the only way…
Yeah, point-free sounds cool, until you actually try it out. Even in their example they are not point-free: compose(foo, bar, baz) Here compose is applied to three "points" (which happen to be functions).
I use point-free languages, and it’s completely okay if you don’t enjoy them. It’s just that I love them, and they’re fun for me, so I don’t like when people are just saying that they’re like objectively bad. Try them out, see if you like them! I personally like a concatenative/stack-based flavor, but you might not! :)
While I agree that sometimes I do break my rules myself, when I don't want to spend 15 minutes to name something, I don't think your example convinced me of your case. Would you honestly expect someone to know by heart what "sort -rn" or "uniq -c" do? This forces the reader to know what all the arguments mean by anyone reading this code. If you'd try to push this code, I wouldn't let it pass code review without a com…
> Would you honestly expect someone to know by heart what "sort -rn" or "uniq -c" do? For people that program regularly in bash, I would. If it was a rare bash script in a code base where many team members didn't know bash well, comments would be appropriate. Even there, though, that's not the same as introducing superfluous intermediate variables. The larger point here relates to "intended audience" or "what compete…
I think we're in agreement on the framework of the debate (rare), it's just that from personal preference we draw different lines on what information is reasonable to assume your reader has.
I personally have not regretted much assuming incompetence in my future self on small things, but it's your choice to what standard you hold yourself against.
Tacit programming + llm control library (like LangChain) has a lot of untapped potential. Tacit programming shines when the control structures are simple (and functions/variables can be anonymized), and these patterns occur frequently in programming on top of llms.
Absolutely every single time when I use functional programming, I store my intermediary calls in a variable, specifically because naming that variable forces me to explain what that intermediary result should be. If the intermediary result makes no sense, and only the function composition makes sense, I'll create a new well named function that does the chaining, even if it's single use. This is literally the only way…
I've been using tacit programming intensively to the extent I get rid of most variables. I use both syntactic threading macros and functional combinators to achieve this. It is a double-edged sword in that it can make code as ugly as the original code it is trying to improve upon, but working without variables isn't that difficult nor does it lead me to intense clusterfucks. Composing lambdas contribute to this a lot…
Could you share the code for some of the custom threading macros you created? Looks very useful.
You are applying functions to arguments, aren't you? So "point-free" means you cannot apply a function to arguments on the left hand side of a definition, but you are allowed to do so on the right hand side? If that's point-free, it is also point-less.
tacit means a definition doesn't name it's arguments, not that it isn't applied to arguments. the word tacit means implied, so arguments are implicity worked upon, instead of explicitly. tacit doesn't mean functions are nullary.
Yes, but that is the same as not applying a function to arguments on the left hand side of an equation, but still doing so on the right hand side. The question is, what is the point of not doing that on the left hand side, if you then go and still do it on the right hand side? Let me say it again: It's pointless.
The first time I've read about tacit programming is in one of the blog posts on oilshell [1]. Really like the idea and apparently one of my favorite features of UNIX is the pipe facility, and it's actually a form of tacit programming. This makes a lot of sense since the OS itself is already full of ready made functions that can be exposed through the API [2]. It's also interesting to note that most of the modern prog…