La notación $f:A\to B$ significa que $f$ es una función que tiene $A$ como dominio y $B$ como codominio.
En cuanto a la razón por la que sentimos la necesidad de especificar un codominio cuando el rango hace el trabajo, eso es un poco más complicado. La respuesta corta es que generalmente es más fácil encontrar un codominio apropiado que el rango, y también suele ser más fácil de escribir.
Como ejemplo, basta con tomar $f(x) = x+\frac 1x$ con el dominio $\Bbb R\setminus \{0\}$ . Claramente, $\Bbb R$ es un codominio, y eso es todo. ¿Cuál es el rango? Puede que ya sepas que es $(-\infty, -2]\cup[2, \infty)$ pero si no lo hace, no es trivial encontrarlo, y es una molestia escribirlo.
Además, ¿qué es lo que realmente necesito ¿para qué? El codominio / rango es relevante, por ejemplo, al componer funciones. Por ejemplo, al calcular $f(f(x))$ . En ese caso podría ser adecuado utilizar $\Bbb R\setminus \{0\}$ como el codominio, pero una vez que lo haces, puedes ver que el dominio y el codominio son iguales, así que componer la función consigo misma no es problemático.
(En otras respuestas se ha señalado la práctica relativamente común de preocuparse por todas las funciones con un determinado codominio, que es mucho más fácil de escribir si tenemos una notación que especifica el codominio en lugar del rango. La mayoría de las veces que hacemos esto, no nos importa realmente si esas funciones son suryentes).
Resulta que el rango exacto de una función no es que importante. Necesitamos saber que las operaciones que pretendemos hacer con la salida de la función son válidas, y ya está. Así que cuando podemos salirnos con la nuestra con un codominio más simple y menos estricto, entonces eso es lo que hacemos.
Por supuesto, decir que $f:\Bbb R\setminus \{0\}\to \Bbb C$ también parece un poco tonto. Así que tiene algún mérito restringir el codominio, al menos un poco.
Existe un paralelismo en la programación. Si está familiarizado con los lenguajes compilados más comunes, probablemente habrá visto algo como
int someFunction(float a) {
...
}
donde entonces sabemos que el codominio de esa función es $\Bbb Z$ (dentro de los límites de la máquina, por supuesto). A menudo no es tan importante saber exactamente qué enteros podrían venir como resultado de esa función (sólo confías en que la función está correctamente implementada, y ya está). Pero el hecho de que sólo devuelva enteros es importante, especialmente para el compilador, donde una línea como someFunction(3.2)+someFunction(4.1)
se convierte en instrucciones de máquina muy diferentes si el codominio se cambia repentinamente a float
.
Por otro lado, si tuvieras alguna forma de decirle al compilador que la función sólo dará números pares, o sólo cuadrados perfectos, entonces eso probablemente no sea de ninguna ayuda para el compilador. (Aunque los compiladores son realmente cosas inteligentes; he visto que reconocen varias implementaciones diferentes de $\sum_{i = 1}^n i$ y realmente escupir las instrucciones de la máquina para $\frac{n(n+1)}2$ de todos modos, podrían exprimir un nanosegundo).