- GNU/Linux + i3wm; complete control over my programming environment.
- bash + GNU coreutils; seriously, take the time to be able to write helpful bash scripts which can run basically anywhere.
- git; use it even when you're not pushing to a remote. Add helpful aliases for everyday commands. Build a good mental model of commits and branches to help you through tough merges. ( my ~/.gitconfig: https://gist.github.com/jeaye/950300ff8120950814879a46b796b3... )
- Regex; combined with bash and your GNU tools, a lot can be done.
- Vim; modal editing and vi-like navigation can blow open your mind. Explore the existing plugins to accomplish everything you want to be able to do. It's all there. ( my ~/.vimrc: https://github.com/jeaye/vimrc )
- Functional programming; if you're new to this, start with Clojure, not one of the less-practical languages. This has made such a huge impact on the way I think about code, write code, and design code that it's possibly the biggest one here for me.
An excellent list. Regarding functional programming, I recommend starting with a gentle approach that doesn't require picking up a new language:
1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
2. Take the time to really understand what side effects are, and start avoiding them everywhere they are not necessary. Keep scopes as local as is practical.
3. When you start toying with functional programming per se, make sure you really have your head around recursion. That's where much of the magic concision comes from.
Agreed on gentle approach. I've started using libraries like Ramda for JS, which is especially designed to ease people into functional patterns.
I then moved to FP-TS, which makes more heavy use of haskell-like patterns and monads.
The hard part isn't the syntax of whatever language, but understanding the new patterns and way of thinking, which you can do with a simulation layer like FP-TS (for typescript/javascript).
The functional patterns and emphasis on types makes your code robust and more correct. It emphasizes correctness, which is ultimately what your job is as a programmer. Optimization comes after.
There could be other reasons for disliking it other than not understanding what’s going on. Maybe other people are working on tight deadlines and don’t have the time or mental energy trying to understand an entirely unfamiliar programming paradigm.
I would want to program in functional too, but I would seek out projects or teams that already use functional. I’d never introduce a functional language to an already established team or project, unless of course, I was the CTO and there were clear benefits.
If you practice domain driven design, you can always start out isolated projects or even just new modules if your code is sufficiently modularized. I started out by making a new library/module with Ramda, and then it's consumed via a function call that anyone programming in any style can use.
Agreed that functional patterns can be hard to understand if you don't know the patterns, but if you do know them they are much easier to understand and reason about the code. It's a long term investment, and one that frankly I believe will be inevitable as more and more people start doing programming work.
Had the same result, and had to settle for the middle ground of (lodash | underscore; I don't recall which now.)
There's a lot of resistance to adopting anything with the name functional, and that resistance is often seen in a /refusal/ to try to understand instead of a mere lack of understanding: people put up walls straightaway. I expect many need motivating examples to guide them to it.
Languages like Kotlin are pointing the way towards that middle ground far more effectively than, say, Scala did.
> I recommend starting with a gentle approach that doesn't require picking up a new language
Disagree. This is likely to 'dilute' the lessons of functional programming, as it were. If you learn to program in idiomatic Clojure/OCaml/Haskell/Scheme, you can be relatively sure you really have picked up the principles of functional programming.
If you merely attempt to apply new principles in an environment you're already comfortable in, things aren't going to 'click' in the same way.
Beside that, plenty of languages simply lack the facilities needed to effectively use the concepts of FP, as vmchale says.
I can confirm this. Although I knew the principles of FP, concepts didn't click until I started using a functional language.
In non-FP languages, I didn't originally appreciate the benefits of the pattern. It was more work to do things functionally, so I dismissed some patterns that were actually useful.
I'm a bit biased, but would recommend Elixir as an accessible FP language. It has an accessible syntax and modern tooling.
You may be frustrated with the constraints of immutability for a few weeks, but the benefits become apparent once you're used to it.
Now when I work in non-FP languages like JavaScript, I will apply FP principles when it makes sense.
Some people say learning Latin makes you a better writer, smarter, etc. even if you're unlikely to directly use it. Dubious claims but it feels like FP can be like that.
The thing about learning Latin (not that I am great at it), at least for an English native speaker, and the epiphany English language speakers have, is the realization that language can have structure and can be discover-able, if I know a root word then I can almost be sure that I know the meaning of con, re, dom etc. There is just no equivalent to that in English other than the Latin origin words we borrowed, because we just assimilate any words we like and make up new ones as we see fit. An example would be beaucoup a Vietnamese word but most US English speakers would know it means big. Without the historical reference of a movie you would not have a reason or rhyme why an Asian origin word made it into English. There is literally no reason or rhyme to most of the spoken language and I think that is the epiphany, that some people actually thought out a logical way to create a language and via that logic it is discover-able.
Very cool. Did not know this. Will have to tell my Vietnamese wife. We were trying to figure out if English had any Vietnamese words. I think this counts.
Sure. When learning Latin, well, you learn to read and write Latin. You absorb the language's principles by learning the language, not by trying to highlight them in a language you already know. That can only give a much shallower appreciation.
Not quite true - many complexities of grammar are shared between the languages and it is often useful to structure the learning of Latin around the English patterns of grammar.
I imagine that diligently learning a foreign language (dead or otherwise) will make you smarter, especially if the free time spent on it would otherwise be spent on less academic pursuits.
A side note: "Dubious" comes from Latin, sharing the root of "duo", which means two,in this case referring to the possible indecision between two things or ideas.
Even in languages where the concepts can be encoded, it can be hard to determine what aspects of a given library are the encoding and which parts are the fundamental ideas if you haven't seen the ideas used in well-suited language. For instance, I didn't really understand the use of functools.reduce[0] or itertools.starmap[1] in Python until I was familiar with zipWith[2] and foldl [3] in Haskell.
The ideas themselves are not particularly complicated, but I hadn't previously worked with abstractions where the default was to operate on whole data structures rather than on individual elements, so I didn't see how you would set up your program to make those functions useful. In addition, for abstract higher-order functions, type signatures help a lot for understanding how the function operates. I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
It's now easy for me to use the same abstractions in any language that provides it because I only have to learn the particular encoding of this very general idea. While I couldn't figure out why functools.reduce was useful or desirable, I couldn't figure out many parts of C++'s standard template library at all. But if you already know the core concepts and the general way that C++ uses iterators and the fact that functools.reduce, Data.Foldable.foldl, and std::accumulate[4] are all basically doing the same thing for the same reasons is a lot more readily apparent.
> it can be hard to determine what aspects of a given library are the encoding and which parts are the fundamental ideas if you haven't seen the ideas used in well-suited language
That's a good point. Using a proper functional programming language doesn't just enable FP ideas (you can't fake a feature like implicit capture of variables), it may also clarify them by reducing baggage.
> I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
I suspect you're just a better Haskell programmer than me (I've only ever dabbled), but I find the big-mess-of-arrows syntax to be pretty confusing compared to a simple tuple of descriptively named identifiers.
Functional programming has been a game changer for me as well and has enabled me to write larger and more complex programs that are easy to maintain and reason around. I highly recommend cytoolz for python
This! What is funny that is that I started doing this before I knew anything at all about functional programming, I just started to avoid stuff that I had painful experiences with.
Later I read a couple of chapter of SICP and then I really changed and my programming hasn't been the same since. The language I use at work is JavaScript and while SICP isn't for JavaScript, nothing else has changed my JavaScript for the better to that degree.
> 1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
I am not very comfortable with this. How can I learn to do this in traditionally non-FP languages like Java? (Am CS undergrad student)
Caveat: I haven't touched Java in years, and that was not even a current version of Java at the time (well, it was old code made to run on the then-current JVM, but not utilizing any features introduced after 2006 or so). I'm assuming these are good resources, but I'm not sure.
I have experience with the same things in C# and other languages, the way they're using them in these articles are what I'd expect from a comparable API.
I can't speak authoritatively about Java, but it looks like map-reduce is available in Java 8 by casting a collection to a stream [0]. Considering the definitions of map and reduce can help one see how they can replace loops/counters:
MAP: Take a collection, say a list/array or a dictionary/hash, and perform some function on each member of the collection, returning a new collection who's members are the return values for each original member. It's a loop, but no loop!
REDUCE: Do the same thing as map, but carry along an output variable, and have your function's output for each member (potentially) mutate that output variable. Summing is a basic example.
I'm not specifically recommending preferring this in Java as a step towards functional programming. It's in, uh, more terse languages like Python and Ruby where the payoff is obvious [1][2]. And among not-functional programming languages, it's not just dynamic languages, either. Consider Dart (and seriously, consider Dart) [3]. Also, Javascript, which has had many features shoehorned-in over the years, has these and related functions.
One other other thing. Functional thinking has greatly changed the landscape of client-server applications that are hosted in the cloud as well. If your aim is apps, maybe don't bother to master the skills needed to set up and maintain a Linux server (although if you follow OP's other suggestions, you're well on your way). Instead, consider your backend as a network of microservices, functions, that each do one thing and do it with side effects only when necessary. The host for your app? Poof! That's AWS/GCP/Azure's problem.
One other thing. You will be thinking functions first if you get into data science, say with Python/Pandas. In general, Pandas functions are vectorized, meaning that they operate on members of a collection in parallel. You really don't want to write a loop that iterates over some 5,000,000 member collection and applies some expensive function serially.
I upvoted the parent and want to emphasize vim key bindings. This is not necessarily vim the editor, it's your editor of choice in vim mode. Learning to use vim is like learning to touch type: it's initially a pain, but it's hard to ever go back once you've mastered the basics.
If you haven't learned to touch type (it happens, I didn't learn until I was 22), then first learn that, then learn vim.
FYI: Remap your capslock key to escape to use Vim more effectively.
I recently had a revelation: When typing longer shell commands it can be time-consuming to go back and make changes, turns out you can use Vim style cursor movements within the Fish:
This was a revelation for me as well. I also didn't realize that if you haven't configured vi keybindings, the default is Emacs (in bash or anything using readline). Even though Vim's my main editor, I found modal editing a bit too heavyweight on the commandline, so I prefer the default Emacs (most useful by far: C-b to go back one word and C-k to delete everything right of cursor).
I prefer the emacs bindings for the command line such as C-A, C-U (mostly due to muscle memory), but have set up my $EDITOR as vim. This allows me to do C-X C-E, which opens the current command in vim to be edited.
If you are using zsh, you need to add this to ur .zshrc
You don't need to know how to touchtype. Programmers are not a glorified typing pool. Furthermore, my father† has worked every job there is in a world class news organization since entering adulthood and never learned to touchtype. He could type 140 WPM.
Imagine having to look at your mouse every time you clicked on something. Now imagine you no longer have to do that.
If you're really curious, just learn to touch type and find out. It doesn't take all that long if you're already a solid typist. I'd be fascinated to read an article from someone who learned how to touch type and thought it was a waste of time.
OK so terminology: touchtype to me means that you learn the fingering the keyboard in a specific way. Not just that you can type by not looking at the keyboard. I can do that. I just never bothered with the "correct fingering".
For me it improved my physical comfort significantly. In particular it solved my back pain because I didn't spend so much time with my head looking down.
One other method to quickly exit insert mode if you can't remap keys is ^C. I did not know this even though I used gvim on Windows computers not under my control for years.
While I do use i3 for more than L/R splits with stacking etc. the largest benefit I get from it is workspace management.
I use i3 with polybar and have dedicated workspace icons (web browser, terminal, editor, to-do, email, music, etc) for quick navigation between different applications. Over time I’ve built up muscle memory (i.e $Mod+3 will bring me to my editor) that has significantly sped up my development process. While you could use another window manager for a similar purpose, I find the relatively minimalist approach of i3 + polybar in my case to be fast and highly configurable.
I can echo this! This is exactly how I feel too - the muscle memory around workspace management and the scratch windows (floating windows that you can toggle in/out of visibility based on a single keystroke) are the real boosters for me rather than splits. Splits are useful but the most common use I've seen myself do is to have a browser and a terminal / editor in splits.
I've got a portrait monitor connected to a laptop, so I end up splitting the monitor's pane vertically.
I treat each workspace as dedicated for a specific purpose - Dev, Browsing, Chat, etc. That gives me quick mnemonics to hop to each space: MOD+1, MOD+2, MOD+3, ...
Within my Dev workspace, I use a tabbed pane for top-level organization: browsers (stacked), IDE, terminal, Emacs (magit + org), etc. This keeps my focus on that space when doing dev, and away from the laptop monitor, which is only occasionally useful as a reference.
I'll occasionally stack a terminal beneath my IDE if the current task requires it, e.g. to test a deployment or a project task.
Off the top of my head, things I use most beyond L/R split include:
1. Workspaces. Not at all unique to i3 but I’ve kept mine themed and automatically load certain apps into the same workspaces—all things i3 makes easy to do.
2. Floating scratchpad for media player. Nice to have my music controls always accessible but only visible when I unhide them.
3. Vertical split beneath my editor with a terminal. Just my personal preference, but I typically have L/R with code/browser and then split the code half vertically.
Being able to move windows across displays and workspaces quickly are other pluses, but again, not at all i3 exclusive.
I moved to awesomewm a few years back and I absolutely love it. I can open half a dozen terminals at once and they'll all automatically be ordered in a way that's immediately useful to me, where all the terminals are visible and (largely, depending on the automatic layout set) equally sized. And if I want another layout, I just press one combination to cycle through all the layouts I've configured. tmux doesn't give me that kind of flexibility. It doesn't feel anywhere as fluid or seamless to switch between half a dozen (or more) terminals.
It means I can have an overview of a bunch of different things and keep terminals context-specific (1 terminal for htop, 1 for docker, 1 for whatever remote test environment, 1 for project A, 1 for project B, 1 for some other remote host I need for some reason, etc.) If I want to do a new task unrelated to anything I'm doing before, I don't need to break the context of an existing terminal, I just press Alt+Enter, it's automatically slotted into a place where it's completely visible and usable and I can do that task quickly. When I'm done, I can close it, again, without disturbing the context of all the other terminals. It's just incredibly freeing to have that and I feel it frees a lot of cognitive load by being able to go back to a terminal for a certain task and immediately see exactly where I was and what I did last.
Also, much like the other comments, I use task-specific virtual desktops all the time. First desktop is for all the terminals. Second is for browser/communication. Third is for project A. Fourth is documentation related to projA. Fifth is projB. Sixth can be more documentation. I often have 10 virtual desktops for different things. I don't want to imagine what it'd look like if I had it all on one desktop.
Sorry to hijack your comment, but do you know of resources I can read to write my own custom layouts using awesomewm? I have a 21:9 monitor, and would like to write a layout where I can have a game running in 1080p + some windows on the side. Currently I do this just fine with floating windows, but surely there must be a way to use the tiling system.
Hah, it's probably possible, but awesomewm's documentation for this kind of thing isn't great. I've looked into it but was put off by the complexity since I don't have that much time to spend on that kind of thing. I did find a couple of links that might be helpful as a jumping off point?
I depart from that L/R split pretty regularly, especially when coding. I frequently have a single large window taking up half of my screen, and several smaller windows stacked vertically on the other half. The big window is usually the editor, but sometimes the browser if doing web development, or possibly a pdf or something like that. The little windows might be the editor, or a browser, or general command line work, or the stdout of a server or other daemon that I'm working with.
I sometimes do move to more of a tmux split workflow, especially if I'm working on remote machines, but it's just much nicer to have the same keyboard commands for all of my windows.
I always have at least 4 windows open in a project workspace, those being a text editor, a file browser, a terminal, and a web browser.
Currently I use i3 with packages pulled in from XFCE to handle sessions and power management, plus xfce4-appfinder and xfce4-panel (started and killed with MOD keys of course) because I wanted something beyond d-menu / b-menu.
Working on high resolution monitors allows me to do LLRR splits. Also the couple second I save from not moving my mouse I think is a worthwhile tradeoff. i3wm's floating mode doesn't really make me _lose_ anything, either. All upsides.
My typical setup is that I have a single desktop for each project I'm working in. Web projects split into 4 panes. Top left, browsers (different chrome profiles running in different i3 tabs), bottom left dev console (same), top right vscode, bottom right, i3 tabs for all the consoles I have open (`npm run`s, git, etc). For go projects I have full left split IDE, ssh in to vagrant top right, bottom right are local consoles (tabbed) for building/running tests/etc.
This would not be possibly if I wasn't using 4k monitors. That was a big shift for me, because now I think of each 4k monitor as 4 1080p displays.
I agree wholeheartedly. Especially when most of the work I do is on a remote server anyway, so I'm already in tmux. I still use i3wm on older laptops just for the battery life gains, but 95% of what I do is Firefox + Terminal emulator, and alt-tab is just as fast. My main workstation is just gnome and it's fine.
(Sidenote: is there any sort of linux libvte-based terminal emulator that has tmux integration a-la iTerm2? For when I do use i3, it would be really nice if I could spawn a new terminal on a remote server, attaching through an existing tmux session.)
Re. Sidenote:
It doesn't look like there are many terminal emulators with tmux built in¹, but you can bind a keyboard shortcut to `xterm -e tmux attach'. (rxvt[-unicode] and st also support the -e flag, to run a given command instead of the default shell).
For a very long time I didn't use anything besides just plain vim, 2 biggest things to add to your vim use is undodir and YouCompleteMe. Crazy that I didn't have either of these for so long, undodir I wish was part of the default.
I made persistent_undo as part of Google Summer of Code in 2011. Very grateful I got to do that (and for the mentorship of Bram Moolenaar), and I'm so glad that this became part of your essential workflow. I also can't live without it at this point.
Thank you so much! It is very much appreciated and I don’t think I have any cases where I wish it was better. It works exactly like I expect and does what it says on the box.
I switched from vim (with YouCompleteMe) to VSCode about 4 years ago and I've recently discovered the intellisense engine for VSCode is now available as a vim plugin: https://github.com/neoclide/coc.nvim.
I've been using vim for 20+ years and have never been into extensive customizations. That was part of the attraction, because I had to deal with a lot of remote servers, often off-shore. With out-of-the box vi/vim I could get the most done with the least number of keystrokes.
Someone made a comment above about IDE editors with vim emulation. Wish every IDE would do that. RStudio for example is not exactly vim, but I find it close enough.
If only Spyder and Jupyter would that.
It lets you modify and save code from the quickfix buffer, so when you search for something and it shows up in the qf, you can do a find replace / edit / etc. This is especially great for mass refactoring / renaming.
By "undodir" are you referring to the "vim-undodir-tree" plugin? Because the "persistent_undo" feature that is built into Vim/Neovim (normally) is what I think of when I hear "undodir".
> Vim; modal editing and vi-like navigation can blow open your mind. Explore the existing plugins to accomplish everything you want to be able to do. It's all there.
The reason I end up ditching Vim after a few weeks every time I try it (4 serious attempts now) and go back to IntelliJ (which I’ve used for two decades) is that I never found a solution to the following trivial issue:
Imagine you have a large Java codebase and you want to refactor all occurrences of a method called “doFoo()” to give it a better name - how do you do this in Vim?
This is a single keypress in IntelliJ and I use this function very frequently but I never found a way to do it in Vim.
Note: I only want to change THIS doFoo() method, not the hundred other doFoo() methods in the codebase.
Also note: yea, this includes all implementations of the interface, all abstract classes, all classes that extend a a class that implements the interface and all other funky polymorphic things, and NO unrelated code. And do it all in one keypress, don’t have me manually go through line by line.
There are others, but this one is pretty good. Next release of Neovim will have one built into the editor. Frankly, it's a bit of a hassle but once you get an LSP provider set up you can get one for just about any language you're using
Im the same, s/vim/emacs/, though I'm also very comfortable in Vim.
I use Emacs for a tonne of stuff, and basically any other language I use. But for Java, with its deeply hierarchical codebases and general verbosity, there's just no sane way to manage that complexity smoothly without a good IDE.
(One could argue that if a language necessitates an IDE to work with it, then that's a failure of the language's DX. But thats an entirely separate discussion.)
Years ago Interlisp (does anybody here remember Interlisp?) had a function called DWIM (AKA Do What I Mean). It was an (optionally enabled) part of the REPL. If you typed something that made no sense, DWIM would try to figure out what you really meant to type, and offered to run the corrected command.
I have often wondered why that functionality disappeared, and why no one has tried to resurrect it. Search engines offer corrections all the time; why doesn't bash?
This poem indicates the frustrations that hackers had with DWIM at the time, which may explain why no one tried to resurrect it. Too-clever-by-half features intended to help tend to drive people nuts, especially when they fail. Even when they succeed, they interrupt the user's flow and become like that dialog box Windows users just dismiss.
alias emcas='emacs'
alias emac='emacs'
alias emasc='emacs'
alias enacs='emacs'
alias emas='emacs'
alias emascs='emacs'
alias eamcs='emacs'
alias eemacs='emacs'
I must forget to give grep an filesystem path argument at least 10% of the time I invoke it. What I'm intending to do in all of those cases is recursively grep in the current directory. "Warning: recursive search of stdin" might be my most-seen console error message.
This used to happen to me constantly. I fixed it accidentally in switching to Ripgrep [0] which defaults to recursively searching the current directory. Bonus: it parallelises too!
Honourable mention also to FZF [1] which not only makes it trivial to locate a file in a directory tree, but has revolutionised my history use with its fuzzy matching.
alias g="git"
__git_complete g _git # enable git autocompletion (braches, etc.)
alias gc="git commit -am"
alias gp="git push"
__git_complete gp _git_checkout # checkout is more useful than _git_push because it autocompletes the branch
alias ga="git add -A"
alias gd="git diff"
alias gb="git branch"
alias gx="git checkout"
__git_complete gx _git_checkout
alias gs="git status"
alias gl="git log"
- bash + GNU coreutils; seriously, take the time to be able to write helpful bash scripts which can run basically anywhere.
- git; use it even when you're not pushing to a remote. Add helpful aliases for everyday commands. Build a good mental model of commits and branches to help you through tough merges. ( my ~/.gitconfig: https://gist.github.com/jeaye/950300ff8120950814879a46b796b3... )
- Regex; combined with bash and your GNU tools, a lot can be done.
- Vim; modal editing and vi-like navigation can blow open your mind. Explore the existing plugins to accomplish everything you want to be able to do. It's all there. ( my ~/.vimrc: https://github.com/jeaye/vimrc )
- Functional programming; if you're new to this, start with Clojure, not one of the less-practical languages. This has made such a huge impact on the way I think about code, write code, and design code that it's possibly the biggest one here for me.