I don’t think optimization in Haskell is much different from any other language. In most cases naive Haskell is pretty fast and memory efficient, but there are some patterns that will make it more or less so. There are a handful of common patterns you can learn that are idiomatic and will generally result in faster or more memory efficient code, and some common libraries that you can use that are more efficient.
There are also some common patterns for less idiomatic but more performant code that you can use as a first pass when you need to optimize things further. Usually that’s sufficient, but when you need to go even further with optimization then it can get hard. Every language can. In Haskell, it usually means starting by dumping core and seeing what the compiler is doing, and getting familiar with the different passes the compiler makes. As a last resort you can also just write in C and use the ffi.
There are also some common patterns for less idiomatic but more performant code that you can use as a first pass when you need to optimize things further. Usually that’s sufficient, but when you need to go even further with optimization then it can get hard. Every language can. In Haskell, it usually means starting by dumping core and seeing what the compiler is doing, and getting familiar with the different passes the compiler makes. As a last resort you can also just write in C and use the ffi.