Despite its lack of mainstream-ness, I find that D, specifically version 2, provides some interesting lessons that can't be easily learned elsewhere. It makes a more serious attempt than any other language I can think of to get imperative/procedural, object-oriented and functional programming to play nice with each other, and to allow programming at a very low level (pointers, manual memory management, inline assembly language) and a very high level (generic and generative programming) in the same language.
This is valuable because, rather than seeing paradigms in isolation as if they existed in different universes, you get to see the forest through the trees. You get to see the strengths and weaknesses of each paradigm at a fine grained level as you blend them into your programs. You get to see how major aspects of paradigms can be implemented in libraries in terms of lower-level code. The standard library module std.algorithm implements important functional programming primitives, yet is straight, simple D code with no magic. Similarly, std.range implements lazy evaluation, but again is fairly simple D code. You get to understand the costs involved in the primitives of each paradigm, because D's close-to-the-metal features make what's really going on under the hood relatively transparent. You can even write something low-level that looks like C, and then create a pretty, high-level interface to it, in the same language, with no magic glue layers getting in the way.