It's interesting what people think programming is and how thinking in Haskell has changed that for me. I remember seeing something about how Mark Zuckerberg wanted to teach kids 0-based counting so they could be computer programmers.
Well guess what, I don't use 0-based counting anymore!
In Haskell instead of x = arr[0] you say something like (x:xs) = arr. You could also say x = head arr, but I find the former idiom happens more often in my code. Getting the first element of a list is a subset of the functionality of destructuring that is as pervasive in Haskell code as array indices are in PHP.
I mention this not because how you get items out of a list in different languages is interesting in itself, but because I used to think that 0-based math is one of the things that makes you a programmer. It turns out I don't need it and I don't miss it.
Other things I am surprised that I don't miss are multi-assignment variables and loops.
I used to think that these were core elements of what it means to program. I used to think that languages without loops were just toys (and a lot of them still are).
Things I still use are if statements and function calls. These seem to be really quite fundamental to what it means to program a computer. There are languages which lack these. For instance, you can use SKI calculus to write computations and that is a language without an if. However, you end up recreating some form of branching combinator if you try to write programs his way.
Things I do miss are strict evaluation and dynamic types.
Lazy evaluation can do some cool things, but it can make your programs more confusing. This especially comes up when debugging things like why they are running out of memory. Sometimes I am implementing a straightforward standard eager evaluation algorithm and it would nice to not have to convert this into a lazy-friendly form. I think this might be more fundamentally difficult than converting from iterative to recursive functions. At least it has been for me so far.
Static typing is great and part of what makes Haskell work well, but sometimes it's a pain. Parsing data structures from dynamically typed languages, for instance JSON, is painful in Haskell. JSON as mixed type arrays and Haskell doesn't, so it's hard to translate between the two, whereas in python you just call loads(jsondata) and you get a native python data structure. So fun and easy! Another place where dynamic typing is nice is function/operator overloading. Haskell has this for numbers with type classes. You can do a + b and as long as they are the same type it will work whether a and b are of type Integer or Float or whatever. However, Haskell also has three kinds of strings (String, ByteString, ByteString.Lazy) and I have to use three different append functions (append, ByteString.append, ByteString.Lazy.append) to concatenate them. Python has two string types (str and bytes) and + works on either (as well as on numbers).
In summary, I used to have some ideas about what programming was and what was important in a language. The more languages I learn the more I realize that the things I were really important are not the important things after all. I used to care a lot about surface-level features like whether a language had lambdas or its support for objects. I've come to realize that when you really get down to it high-level languages are just ways to glue together what is essentially chunks of C code underneath. I'm willing to try out different sets of abstractions until I find the one that gets me to my goal with minimum time and effort. Haskell has proven to be a very practical language for getting things done, but python is still ahead of the game for messing around with data in text files.