This is impossible without using undefined
as another commenter mentioned. Let's prove it by counter-example. Assume there were such a function:
f :: a -> a
When you say that's it not the same as id
, that implies that you cannot define:
f x = x
However, consider the case where a
is the type ()
:
f () = ...
The only possible result f
could return would be ()
, but that would be the same implementation as id
, therefore a contradiction.
The more sophisticated and rigorous answer is to show that the type a -> a
must be isomorphic to ()
. When we say two types a
and b
are isomorphic, that means that we can define two functions:
fw :: a -> b
bw :: b -> a
... such that:
fw . bw = id
bw . fw = id
We can easily do this when the first type is a -> a
and the second type is ()
:
fw :: (forall a . a -> a) -> ()
fw f = f ()
bw :: () -> (forall a . a -> a)
bw () x = x
We can then prove that:
fw . bw
= \() -> fw (bw ())
= \() -> fw (\x -> x)
= \() -> (\x -> x) ()
= \() -> ()
= id
bw . fw
= \f -> bw (fw f)
-- For this to type-check, the type of (fw f) must be ()
-- Therefore, f must be `id`
= \f -> id
= \f -> f
= id
When you prove two types are isomorphic, one thing you know is that if one type is inhabited by a finite number of elements, so must the other one. Since the type ()
is inhabited by exactly one value:
data () = ()
That means that the type (forall a . a -> a)
must also be inhabited by exactly one value, which just so happens to be the implementation for id
.
Edit: Some people have commented that the proof of the isomorphism is not sufficiently rigorous, so I'll invoke the Yoneda lemma, which when translated into Haskell, says that for any functor f
:
(forall b . (a -> b) -> f b) ~ f a
Where ~
means that (forall b . (a -> b) -> f b)
is isomorphic to f a
. If you choose the Identity
functor, this simplifies to:
(forall b . (a -> b) -> b) ~ a
... and if you choose a = ()
, this further simplifies to:
(forall b . (() -> b) -> b) ~ ()
You can easily prove that () -> b
is isomorphic to b
:
fw :: (() -> b) -> b
fw f = f ()
bw :: b -> (() -> b)
bw b = \() -> b
fw . bw
= \b -> fw (bw b)
= \b -> fw (\() -> b)
= \b -> (\() -> b) ()
= \b -> b
= id
bw . fw
= \f -> bw (fw f)
= \f -> bw (f ())
= \f -> \() -> f ()
= \f -> f
= id
So we can then use that to finally specialize the Yoneda isomorphism to:
(forall b . b -> b) ~ ()
Which says that any function of type forall b . b -> b
is isomorphic to ()
. The Yoneda lemma provides the rigor that my proof was missing.
myfunction _ = undefined
is pretty much the only other possible function with that type signature. – huona -> b
it's most general signature, but you can restrict it toa -> a
. – huonf x = seq x x
is special because it is strict inx
, unlikef x = id x
which AFAIK is not.f x = seq b x
is more likef x = id x
in this case, it is theseq x x
which is special. So I see three different functions, without playing withunsafe
things: const bottom (f x = const undefined x
), lazy identity (f x = id x
), and strict identity (f x = seq x x
). – CesarBseq x x
isn't strict inx
, see the last sentence of the second paragraph. – huon