1. 1

    Bozhidar, why do you care about web-ui at all? Why are you not using gnus, notmuch, mu4e, whatever…? Don’t you know, whenever a prominent Emacs hacker does something outside of Emacs (e.g., stares at email in web-ui), a baby comes to life with a speech defect and later starts hating all Lisp programmers, because, unlike the kid, they are not lispers at all.

    1. 2

      I have to admit I stopped using Emacs as an email client at least 10 years ago. :D

    1. 2

      “Why be Like Elm?”, is that a typo? Perhaps meant to be: “Why Do We Like Elm?”

      1. 6

        I read “Why be Like Elm?” roughly as “Why would you, NoRedInk, want to write your Haskell like you write your Elm?”

        1. 5

          It was intended as “Why would I, a programming language, aspire to be like Elm?”.

          A bit farfetched perhaps :).

          1. 3

            I don’t think so, because many of their libraries aim to bring Elm flavours into Haskell.

          1. 1

            Sounds more like a cult to me?

            1. 5

              Why? Is there something in the article that paints it as a cult? What’s in your opinion, makes Clojure ecosystem and/or the community around it sectarian? How is it different from Golang, Swift, C#, Ruby, Python, Haskell, etc.?

              1. 2

                I didn’t mean to say I think Clojure is a cult, I don’t believe that. But that article makes it seem so, to me. A little bit too strong on the “if you don’t agree you’re not clojuring hard enough” angle.

              2. 1

                Sure accurate. Cults normally are not traditionally productive.

                1. 3

                  I’m not sure I follow your comment. Are you saying that Clojure makes you more productive by breaking established conventions, standards, or practices?

                  I think it’s quite the opposite. Clojure follows a long-lasting tradition of Lisp. Lisp always has been a powerhouse, like it’s meant to be the best tool for quickly prototyping programs.

                  1. 3

                    I think Clojure is to lisp as javascript is to java. Real Lisp programming is built around the cons cell. Clojure is built around the underlying available data structures, with a tidy interface for using them. Clojure is built around the seq. Lisp is built around the cons. Clojure uses some of the best of lisps, like consistently low syntax, wrapping expressions as forms using parens. Sure, it looks like a lisp, and quacks a bit like a lisp, but lisp hackers are likely to find it a different experience.

                    As for the cult aspect, I mean Clojure helps make people productive by: encouraging flattening the call tree, supporting interactive development including of running systems (test why something fails in prod, from inside!!), discouraging mutating state, and encouraging cutting through the waste of so many APIs with simple clean interfaces to the world. Clojure helps make people productive by discarding the useless faffle we’ve come to associate with properly designed systems.

                    1. 7

                      Clojure helps make people productive by discarding the useless faffle we’ve come to associate with properly designed systems.

                      This is one thing I’ve found to be true of the Lisp world in general; there’s often a lot of attention given to the APIs, to make them clean and simple, and reducing things to the essence.

                      That’s more of a cultural thing, I guess; I’ve seen similar sane APIs in other languages (Python’s requests library comes to mind), and I’ve also seen annoying to use or badly designed APIs in Lisps.

                      As to the article, it reeks of smugness and the writing style is hyperbolic and lazy (as others have pointed out, even the quoted research seems to be shoddy). In my experience, what matters far more than the language is the experience and synergy of the team. A team of two experienced developers who have a mindset of shipping can get infinitely much more done in any language than a large team of less experienced developers or even a small team of experienced developers who don’t communicate well, have very different worldviews or who let their egos get in the way of building code that works.

                      Of course, a well-oiled team will typically gravitate towards the language they (as a team!) find most productive. Ironically, that doesn’t have to be all (or any) of the members’ favourite language.

                      1. 1

                        Real Lisp programming is built around the cons cell.

                        What do you mean by this? I’ve been hacking on Common Lisp for years and this doesn’t mean anything to me. If you mean the literal cons cell data type, I strongly disagree - you could replace the cons cell with a Python-like list where you can’t reify a reference to the tail/cdr and most people would be 99% as productive. If you mean “the list datatype in general”, then I’m less opposed but still not convinced.

                1. 8

                  You should learn a new language only if it learns you something along the way. Some language can be put in a “bag” of similar patterns:

                  • Python/Javascript/Ruby/PHP/Perl; common scripting language spirit
                  • C/C++/Java/Go; compiled in spirit, feel “not modern” quite low level
                  • Clojure; has my preference to have fun while learning new concepts along the way, should make you quite productive
                  • Haskell; if you prefer never finish your work but learn a lot of things along the way trying and feel good about yourself because you mastered the beast (partially)
                  • Rust; if you still like to learn a low level imperative languages but with a lot better compiler that checks common mistakes for you so your CRUD API will probably be a lot faster and use a lot less resources than using Python
                  1. 2

                    if you prefer never finish your work

                    I’m not sure this is fair, I’ve been developing using Haskell for about 6 years now and we’ve completed plenty of projects. In fact, one of Haskell best features is the ability to fearlessly refactor; when new requirements come up, we just make the obvious change and the compiler guides up to all the places we forgot about. This makes experimentation also very cheap, but only once you’ve learned the language enough to be proficient and know the patterns to use and avoid to help the compiler help you (big one for me is always using total case statements, unless it’s a situation where you know there’s a single thing you want to match and everything else is invalid).

                    1. 1

                      Learning Haskell to the level of pragmatic productivity for an average software developer sometimes literally takes years.

                      Not a single experienced Haskellite would argue with that sentence. Anyone who would - probably hasn’t ventured deeply enough into quite complicated ecosystem of Haskell.

                  1. 48

                    I learned how to balance a red-black tree in college, 20+ years ago, and that’s the last time I ever balanced a red-black tree. Unless the job is writing data structures libraries, why would you ask me that?

                    I’ve built large, production systems used in the most secure environments in the world. My code is secure, performant, accurate, and safe…but no, I don’t remember how to find all palindromes in a string off the top of my head.

                    I remember interviewing at one of the Big Companies. I said I knew C. They asked me which format specifier in printf would do some obscure thing. I didn’t remember. Guess what? I’ve been writing C for…26? years now and I still sometimes look at man pages. I’d be more worried about a developer who didn’t, honestly.

                    1. 19

                      Unless the job is writing data structures libraries, why would you ask me that?

                      Additionally, if I asked an engineer to build a data structures library with red-black trees and they started coding without immediately reaching for a description of the operations, invariants to maintain, etc, for a proper red-black tree, I’d be really nervous. It’s like when a waiter doesn’t write down your order.

                      1. 5

                        To be fair, an experienced waiter can probably keep your order in their head…

                        1. 22

                          I know that in some places it’s considered a badge of honour to be able to take everyone’s order without writing it down, but for many customers it just makes the service worse. I literally do not care how my order gets to the chef. All I care about is that it is correct. Writing it down increases my confidence that it will be correct, meaning that in the wait between ordering and getting my food, I can relax, confident that in due time they will bring me the right things, instead of worrying that I’m going to have to spend my evening negotiating with the waiter and waiting for the chef to get my order right by trial and error.

                          In a similar fashion, remembering how to implement a selection of obscure algorithms is really low on the list of priorities for a software engineer. You could almost argue that for interview purposes, you want a problem the interviewee hasn’t met before, so you can actually observe how they go about solving it.

                          1. 9

                            instead of worrying that I’m going to have to spend my evening negotiating with the waiter and waiting for the chef to get my order right by trial and error

                            Everybody’s got their own thing going on, but I can’t help thinking you might be optimizing for the wrong kind of restaurant experience.

                            1. 5

                              At risk of breaking the metaphor, we should optimize for safety first: Don’t serve allergens to patrons who indicate food allergies. This suggests that orders should be written down or tabulated in point-of-sale systems, rather than memorized, and that orders should be systematically assembled rather than designated by petnames.

                          2. 13

                            Whether the waiter can or not, I trust the process less if they don’t write it down.

                            1. 2

                              I think this might be a “restaurant as status experience” thing? The waiter shows off their memory, this demonstrates that they’re a good waiter, which makes this a good restaurant, which makes you a person who eats at a good restaurant.

                              1. 9

                                I don’t know if it is an US thing but having been waiter/bartender/manager in multiple bars and restaurants in Europe, I think a lot of folks here seems to have had a bad experience with waiters or hold a grudge on how to optimize the certainty of having exactly what they said done. And for siblings comments comparing taking notes as a SE and waiters, I would love to see how SE making minimal wage and living on tips would learn to optimize their workflow.

                                I have been trained to not write down for any table under five people. I insist on trained, first we were allowed to take note for anything and after a few days/a week, you stop taking notes for any table under four etc. It began to be a challenge between colleagues. Bref, all in all you develop a kind of memory palace of your restaurant and their table and make weird association between guest and their commands, your optimize also your work and your are faster and more precise at the end of the day because you will remember longer. Heck, I still have in my memory the set up of tables of all the place I worked in and the orders of regulars and from random people that I happen to see in the city I was living burned in my head years later.

                                As a manager, my rule to train my team was to be able to take order for table of X where X was our average table. It was speeding up the process, making waiter more aware of the flow of his tables, better balance the workload to the bar and the kitchen by timing when to take some orders, reshuffle orders order to let a two people table bypass the ten people table before it reaches the kitchen or later with kitchen. And bartenders and cooks have to learn and do the same. A restaurant is never a FIFO sequential process, you have to manage a concurrent/parallel environment when everybody needs to be served at the right timing, within a known acceptable lag. Having waiters able to memorize your order but also remember it as long as you are in the restaurant, it is similar to the cookie session in your browser.

                            2. 0

                              Zeigarnik effect. Waiters don’t have to analyze, break down, reshuffle and regroup their orders. Software developers do that all the time. I don’t trust those who don’t take notes. All software developers take notes. Good and bad ones. Those who don’t - are not software developers, at best, they are code-monkeys.

                          3. 9

                            Also, red-black trees suck. Keeping the colour in every tree node bloats the data – quite probably by 8 bytes for the struct size on a modern machine, and many malloc libraries round up to the next multiple or 16 or 32 bytes. And both red-black and AVL algorithms are complex.

                            Hash tables are generally more useful now, and b-tree like structures make more sense given cache line sizes (or VM pages), but if I do require a balanced binary tree then my go-to now is the scapegoat tree. The code is much simpler and smaller, there is nothing extra in each node, and it requires only a few bytes of extra global storage (for powers of your acceptable unbalance factor) for all trees, plus one integer per tree if you will be allowing nodes to be deleted. I can and have written complete bug-free code for scapegoat tree in 5 minutes in programming contests/exams where standard libraries were not allowed to be used.

                            But, yes, the main point here is that if I need to write code for a data structure or algorithm for my actual job then I research the literature, find the best thing, implement it very carefully (possibly with modifications), put it into production, AND THEN FORGET THE DETAILS to make room in my brain for the next task.

                          1. 10

                            I’m always happy to see Racket going forward. It’s the best language (I know of) for learning about languages, especially with How to Design Programs. I write Clojure at $dayjob but find Racket more enjoyable and cleaner as a Lisp.

                            AFAICT Racket is a reasonable choice for real-world applications, but it’s not very commonly used. Common Lisp is more common (no pun intended) though still rare. (Anyone else hate how no pun intended is always a lie?)

                            1. 14

                              Oh, I’m cons-tantly making lisp puns.

                              1. 6

                                ☝️ Yes, officer, this is the criminal right here.

                                1. 4

                                  Perhaps you meant to say: “Yes, cons-table…”

                                  1. 3

                                    criminal? cdo?

                                    explain!

                              1. 2

                                I know this is a Clojure post, but out of curiosity I wrote it in Go (what I’ve been working in lately) just to see if I could solve it quickly. Took about 10 minutes, subtract 3-4 for fighting with runes:

                                package main
                                
                                import "fmt"
                                
                                type result struct {
                                	letter string
                                	count  int
                                }
                                
                                func main() {
                                	const input = "aaaabbbcca"
                                
                                	var ret []result
                                	currentLetter := string(input[0])
                                	countCurrentLetter := 1
                                
                                	for _, elem := range input[1:] {
                                		elemAsString := string(elem)
                                		if currentLetter == elemAsString {
                                			countCurrentLetter++
                                		} else {
                                			ret = append(ret, result{currentLetter, countCurrentLetter})
                                			currentLetter = elemAsString
                                			countCurrentLetter = 1
                                		}
                                	}
                                
                                	ret = append(ret, result{currentLetter, countCurrentLetter})
                                
                                	fmt.Printf("%+v", ret)
                                }
                                

                                It’s not particularly elegant, but it works.

                                1. 2

                                  It’s not particularly elegant, but it works.

                                  That’s my problem with many other (non-Lispy) languages. The programs are not elegant, even though they do work. What works for a computer, don’t always work for me.

                                  1. 1

                                    Okay, I am 5 months late, but this code is terrible and I must object to it because there’s no good Go code in this thread. You are mixing up two problems, lexing a token, and figuring out the next token. Apart from that, the code is very nonIdiomaticWithTheseVariableNames, but more importantly blows up on non-ASCII strings.

                                    Here’s two solutions, one imperative: https://play.golang.org/p/-zdWZAnmBip, and one recursive: https://play.golang.org/p/TBudEZBphv7.

                                    The proposed solutions:

                                    1. actually work on unicode input
                                    2. there’s no else in sight.
                                    3. all ifs are early returns.
                                    4. all loops are exhaustive, no weird [1:] boundary conditions.
                                    5. I don’t have to keep around accumulators for the results.
                                    6. no useless types.
                                    7. much easier to read because the code just tells you what it does, why it does it is obvious.
                                    1. 2

                                      Cool, I guess?

                                      I didn’t say I solved it well. I hacked something together.

                                    2. 1

                                      Here’s my take on it. It took me (roughly) the same 8-10 minutes to type it in the web-ui. In Emacs I could shave some time off of it.

                                      type tuple struct {
                                      	s string
                                      	i int
                                      }
                                      
                                      func splitStringReturnTuples(str string) []tuple {
                                      	str = " " + str
                                      	res := []tuple{}
                                      	for i := 1; i < len(str); i++ {
                                      		if str[i] != str[i-1] {
                                      			res = append(res, tuple{string(str[i]), 1})
                                      		} else {
                                      			res[len(res)-1].i++
                                      		}
                                      	}
                                      	return res
                                      }
                                      

                                      Runnable code at the go playground

                                      1. 2

                                        This loops over the bytes in the string instead of the runes in the string. Try inserting a multi-byte rune such as 本 in the string, and see what happens.

                                        1. 2

                                          The problem statement clearly stated the data set, there was no multi-byte symbols. But the tweet gave a clear understanding that interviewer expects solution to be give in a limited time frame. Therefore the solution was provided in terse notation with abbreviated variables taking provided test input and returning expected output. Not more nor less.

                                          But point is taken. Here’s the code correctly handling multi-byte encodings. The logic is the same, but the part of casting passed string into a slice of runes.

                                          When I interview people I don’t expect them to write code perfectly handling every possible input in the limited time. What I’m interested in first, if they are able to come up with straightforward solution leveraging data structures and algorithms helping them solve the problem with optimal complexity. Second, if they can clearly communicate their approach. And coding comes third.

                                          1. 2

                                            That makes sense. I did not mean to criticize your solution in particular, just highlight that this is a common “gotcha” in Go. Casting strings to []rune or looping with for _, r := range str is, as far as I know, the only built-in way to access the letters in strings correctly. I’ve seen many problems arise from assuming that str[x] returns a rune instead of a byte. I think it would be more useful and intuitive if []byte(str)[x] was needed to return a byte, while just str[x] could be used to return a rune.

                                    1. 1

                                      Does Emacs maintain backwards compatibility with old package code?

                                      1. 3

                                        Emacs is extremely democratic and egalitarian, self-regulating ecosystem. If something breaks, there’s always someone who’d fix it. Good ideas rarely die here, they thrive and evolve. And to answer your question: yes, Emacsen are very strict about maintaining backwards compatibility. If added feature to emacs-core makes it stop working with some popular package, there’s always a discussion in emacs-dev mailing list, and such issues quickly get addressed.

                                      1. 21

                                        I recently discovered and appreciated expand-region.el’s “maintenance warning”:

                                        I use this package every day, and have been doing so for years. It just works. At least, it works for all my use cases. And if it breaks somehow, I fix it.

                                        However, it has become painfully clear to me that I don’t have time to fix problems I don’t have. It’s been years since I could keep pace with the issues and pull requests. Whenever I try, I keep getting feedback that my fix isn’t good enough by some standard I don’t particularly care about.

                                        So, I have closed the issue tracker and the pull requests. I hope you can happily use this package, just like I do. If it doesn’t work for you, then I’m sorry. Thankfully Emacs is infinitely malleable, you can probably fix it yourself.

                                        TLDR: I am still maintaining this package, but I am no longer crowdsourcing a list of issues.

                                        1. 4

                                          At least, it works for all my use cases

                                          incidentally, there was one thing in expand-region.el that bugged me for quite some time and yesterday I finally sat down and refused to give myself a break until I fixed it.

                                          ;; Expand region for whatever reason ignores lines when expanding, it should
                                          ;; start expanding from a word, then to a line, then to a paragraph, and so
                                          ;; on. But default implementation ignores the line expansion.
                                          
                                          (defun er/mark-line ()
                                            "Marks entire 'logical' line."
                                            (interactive)
                                            (evil-end-of-line)
                                            (set-mark (point))
                                            (evil-first-non-blank))
                                          
                                          (setq
                                           er/try-expand-list
                                           '(er/mark-word
                                             er/mark-symbol
                                             er/mark-symbol-with-prefix
                                             er/mark-line
                                             er/mark-next-accessor
                                             er/mark-method-call er/mark-inside-quotes
                                             er/mark-outside-quotes er/mark-inside-pairs
                                             er/mark-outside-pairs er/mark-comment er/mark-url
                                             er/mark-email er/mark-defun))
                                          
                                        1. 12

                                          That is not surprising at all, with all the marketing push from JetBrains and Google. What surprising is that Clojure (with virtually zero marketing) quietly had become more popular than Scala. Well deserved, yet still surprising.

                                          1. 2

                                            Emacs and Org-mode. Learning how to track time and starting to use org-pomodoro and meticulously taking notes made me better organized and enforced strict discipline for splitting tasks to smaller sub-tasks.

                                            I always knew that keeping granularity of a task to the minimum is the key to finish it faster, yet every other tool I have tried before has failed for me. Pencil and notepad is okay but my writing is so bad, that it discouraged me to ever read my notes. And searching through the notes not easy. Also I’ve tried plethora of software tools but every single one had some quirks that that annoyed me one way or another.