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?
This is an example of how surjectivity can be expressed in Agda:
For example,
idis surjective (a simple proof):Taking another identity function which works only for surjective functions:
we can pass
idtoid-for-surjective'swith its surjectivity proof as witness:so that
id′is the same function asid:Trying to pass a non-surjective function to
id-for-surjective'swould be impossible: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.Bijectionand other modules).