Search code examples
haskelltypestype-inferencetype-variables

Haskell: example of function of type a -> a, besides the identity


I've just started playing a little with Haskell... I want to write a function of the same type of the identity. Obviously, not equivalent to it. That would be something like,

myfunction :: a -> a

I cannot come up with an example in which the parameter and the return type are the same and can be virtually anything (this excludes the possibility of using Haskell's Typeclasses).


Solution

  • 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.