To answer such questions, you'll need a foundation:
what does it mean that an expression e
has type t
?
You could give arbitrary answers for primitives like getProgName
, but you don't have to.
Instead, look at the meanings of expressions and of types.
Say that an expression e
has type t
when the value denoted by e
(i.e., the meaning of e
as a mathematical value) belongs to the collection denoted by t
.
Now consider t == String
.
What meaning do we want String
to have?
Again, there's a lot of room for choice here.
However, there's a lot of merit in choosing the useful candidate with the simplest mathematical definition.
In Haskell String == [Char]
, so we're really talking about the meanings of []
and Char
The most compelling simple candidates I know of are that []
denotes lists (sequences), and Char
denotes characters, and consequently that String
denotes sequences of characters, i.e., "strings".
With the choice that String
means strings, now we're in a position to ask whether it's possible that getProgName :: String
.
If so then the meaning of getProgName
must be a sequence of characters.
However, there is no single sequence of characters that captures the intent behind getProgName
.
(Edit: Presumably, you want getProgName
to yield different strings when it appears in differently-named programs.)
Therefore, we have to either choose a different type, such as (but not necessarily) IO String
, or we have to choose a more complicated meaning for String
.
The latter route might at first seem appealing, until you consider the deep implications.
Purely functional (more accurately "denotative") programming supports practical, rigorous reasoning thanks to using simple meanings like sequences rather than complex meanings like functions from some sort of environment including operating system, machine execution context.
For closely related remarks and discussion, see the blog post Notions of purity in Haskell.
Edit: I think Peter Landin (grandfather of Haskell) expressed it best with his definition of "denotative programming" (or "genuinely functional programming"), which he recommended as a substantive replacement to vague terms like "declarative" and "functional" programming.
For a reference, quotations, and brief commentary, see this comment in the post Is Haskell a purely functional language?.