I have only played with Pike, never written a real program with it, but it has many interesting features. The best is tagged unions. This is a weak version of what is called an Algebraic Data Type in the functional world.
Imagine you have a piece of memory in C, and you would like to store either an integer or a string (pointer) in it. You can do this with a union:
union {
int myint;
const char *mystr;
} foo;
Then you can write: foo.mystr = "hello world";
To store a string. The problem is you can then access it as an integer:
foo.mystr = "hi";
printf("foo=%d", foo.myint);
This doesn't make any sense. Pike uses its type system to stop you from doing this:
int main() {
int|string foo;
foo = "hello";
write("foo=%d\n", foo);
return 0;
}
Gives:
sprintf: Wrong type for argument 2: expected integer, got string.
src/modules/files/file.c:3913: Fd(1)->write("foo=%d\n","hello")
test.pike:5: /home/crawshaw/test()->main()
If you change the %d to a %s, you get the result you would expect.
That said, the type system is not as powerful as it could be. Much testing is done at runtime, and the use of match cases are not enforced (as they would be in ML/Haskell). If you're interested in languages that stretch the mind, try Haskell. If you want a gentle introduction to fancy type systems and you're from a C background, you may want to experiment with OCaml. If you come from Java, try Scala. Pike is far more of an 'everyday' language for the C programmer than these languages, though they are all useful. (Indeed, my day job is OCaml programming.)
As with all uncommon languages, Pike has the drawback of being, well, uncommon. Fewer libraries, fewer jobs. But that should not deter you from experimentation!