Yes, it is indeed impossible. Consider the following definition of State
:
newtype State s a = State { runState :: s -> (a, s) }
To extract
the value this data type we would first need to supply some state.
We can create a specialized extract
function if we know the type of the state. For example:
extract' :: State () a -> a
extract' (State f) = f ()
extractT :: State Bool a -> a
extractT (State f) = f True
extractF :: State Bool a -> a
extractF (State f) = f False
However, we can't create a generic extract function. For example:
extract :: State s a -> a
extract (State f) = f undefined
The above extract
function is generic. The only state we can supply is ⊥ which is incorrect. It is only safe if the function f :: s -> (a, s)
passes along its input transparently (i.e. f = (,) a
for some value a
). However, f
may take some state and use it to generate some value and a new state. Hence, f
may use its input non-transparently and if the input is ⊥ then we get an error.
Thus, we cannot create a generic extract
function for the State
data type.
Now, for a data type to be an instance of Traversable
it first needs to be an instance of Foldable
. Hence, to make State
an instance of Traversable
we first need to define the following instance:
instance Foldable (State s) where
foldMap f (State g) = mempty
-- or
foldMap f (State g) = let x = f (extract g) in mconcat [x]
-- or
foldMap f (State g) = let x = f (extract g) in mconcat [x,x]
-- or
foldMap f (State g) = let x = f (extract g) in mconcat [x,x,x]
-- ad infinitum
Note that foldMap
has the type Monoid m => (a -> m) -> State s a -> m
. Hence, the expression foldMap f (State g)
must return a value of the type Monoid m => m
. Trivially, we can always return mempty
by defining foldMap = const (const mempty)
. However, in my humble opinion this is incorrect because:
- We aren't really folding anything by always returning
mempty
.
- Every data type can be trivially made an instance of
Foldable
by always returning mempty
.
The only other way to produce a value of the type Monoid m => m
is to apply f
to some value x
of the type a
. However, we don't have any value of the type a
. If we could extract
the value a
from State s a
then we could apply f
to that value, but we already proved that it's not possibly to define a generic extract
function for State s a
that never crashes.
Thus, State s
cannot be made an instance of Foldable
and consequently it cannot be an instance of Traversable
.