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

后端 未结 5 1901
情书的邮戳
情书的邮戳 2020-12-14 08:03

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 lik

5条回答
  •  失恋的感觉
    2020-12-14 08:24

    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.

提交回复
热议问题