Let's say I have a higher order function f :: (a -> b) -> (a -> b)
. But f
only behaves properly if the input function is surjective. Is there anyway to force this to happen in Haskell? For example, I really want f
's type signature to be something like:
f :: (Surjective (a -> b)) => (a -> b) -> (a -> b)
But this doesn't work because I don't want all functions of the type a -> b
to be declared to be surjective, only some of them. For example, maybe f
converts a surjective function into a non-surjective function.
We could wrap the functions in a special data type data Surjective f = Surjective f
, and define
f :: Surjective (a -> b) -> (a -> b)
but this would make it difficult to assign multiple properties to a function.
Is there any convenient way to do this in practice? Is this even possible in theory?
To prove that a given function is surjective, we must show that B ⊆ R; then it will be true that R = B. We must therefore show that an arbitrary member of the codomain is a member of the range, that is, that it is the image of some member of the domain.
To prove a function is injective we must either: Assume f(x) = f(y) and then show that x = y. Assume x doesn't equal y and show that f(x) doesn't equal f(x).
Better: f:A→B is injective if and only if for all x,y∈A, f(x)=f(y) implies x=y.
This is an example of how surjectivity can be expressed in Agda:
module Surjectivity where
open import Data.Product using ( ∃; ,_ )
open import Relation.Binary.PropositionalEquality using ( _≡_; refl )
Surjective : ∀ {a b} {A : Set a} {B : Set b} → (A → B) → Set _
Surjective {A = A} {B = B} f = ∀ (y : B) → ∃ λ (x : A) → f x ≡ y
For example, id
is surjective (a simple proof):
open import Function using ( id )
id-is-surjective : ∀ {a} {A : Set a} → Surjective {A = A} id
id-is-surjective _ = , refl
Taking another identity function which works only for surjective functions:
id-for-surjective's : ∀ {a b} {A : Set a} {B : Set b} → (F : A → B) → {proof : Surjective F} → A → B
id-for-surjective's f = f
we can pass id
to id-for-surjective's
with its surjectivity proof as witness:
id′ : ∀ {a} {A : Set a} → A → A
id′ = id-for-surjective's id {proof = id-is-surjective}
so that id′
is the same function as id
:
id≡id′ : ∀ {a} {A : Set a} → id {A = A} ≡ id′
id≡id′ = refl
Trying to pass a non-surjective function to id-for-surjective's
would be impossible:
open import Data.Nat using ( ℕ )
f : ℕ → ℕ
f x = 1
f′ : ℕ → ℕ
f′ = id-for-surjective's f {proof = {!!}} -- (y : ℕ) → ∃ (λ x → f x ≡ y) (unprovable)
Similary, many other properties can be expressed in such manner, Agda's standard library already have necessary definitions (e.g. Function.Surjection
, Function.Injection
, Function.Bijection
and other modules).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With