¿Es una función inmediatamente impura si toma una función como parámetro?

17

Dado que la pureza de un parámetro de entrada es desconocida hasta el tiempo de ejecución, ¿se considera una función inmediatamente impura si toma una función como parámetro de entrada?

Relacionado: si una función aplica una función pura que se define fuera de la función, pero no se pasa como parámetro, ¿sigue siendo pura si cumple los criterios de no tener efectos secundarios y la salida depende únicamente de la entrada?

Por contexto, estoy escribiendo código funcional en JavaScript.

Dancrumb
fuente
Como un trivial contraejemplo, considere:foo = function(function bar){ print(bar.toString()) }
David dice Reinstate a Monica el
1
@DavidGrinberg Ese no es un contraejemplo, creo, y en realidad destaca un problema mayor; Si tiene funciones que se pueden anular y no puede garantizar que las implementaciones estén libres de efectos secundarios, tampoco puede garantizar que la mayoría de las funciones que toman objetos y llaman a sus métodos sean puras. ¿Quizás bar's toString () elimina algunos archivos del disco?
Joshua Taylor
3
@DavidGrinberg Pero, creo que estás pensando en una buena dirección. foo = function(function bar) { return 3; } es puro y toma una función como argumento.
Joshua Taylor
@JoshuaTaylor Fair point, no pensé en eso. Pero ya has resuelto el problema esencialmente. Como solución alternativa, simplemente llame a la 'raíz' toString()(es decir, la que encontraría en el Objeto de Java).
David dice que reinstale a Monica el

Respuestas:

22

Siempre que todos los valores utilizados en la función estén definidos únicamente por sus parámetros, es una función pura.

La faceta de que la salida es la misma cada vez para la misma entrada se controla si los parámetros son puros. Si asume que los parámetros (como un argumento de función) también son puros, entonces es puro.

En un lenguaje como Javascript donde la pureza no se aplica, esto significa que es posible hacer que una función pura tenga un comportamiento impuro invocando una función impura que se pasa como parámetro.

Esto significa efectivamente que para los lenguajes que no imponen la pureza (es decir, casi todos), es imposible definir una función pura que invoque funciones pasadas como argumentos. Todavía es útil escribirlos lo más puro posible y razonar sobre ellos como funciones puras, pero debe tener cuidado porque la suposición de que es pura se romperá si pasa los argumentos equivocados.

En mi experiencia en la práctica, esto no suele ser un gran problema: me resulta raro que las funciones impuras se utilicen como argumentos de función para funciones puras.

Daenyth
fuente
Con respecto a su afirmación de que "siempre y cuando todos los valores utilizados en la función estén definidos únicamente por sus parámetros, es una función pura". ¿Qué pasa en el caso de las constantes? Si tengo una función areaOfCircle r => Math.Pi * r * r, ¿será areaOfCircleno pura ya que no solo usa parámetros?
David Arno
2
@DavidArno Ese es un punto justo. Según la transparencia referencial, hacer referencia a un valor externo estático no sería diferente a tenerlo codificado, por lo que aún sería puro.
Daenyth
1
"Esto significa que es posible hacer que una función pura tenga un comportamiento impuro". Por definición, una función pura no puede tener un comportamiento impuro. Estás cometiendo el error de pensar que una función f(f2)que invoca f2no depende de forma transitiva de nada en lo que se f2basa. Una función que podría invocar funciones arbitrarias pasadas no es pura.
user2357112 es compatible con Monica el
2
@Daenyth: Mejor, pero aún asume que la función tiene que invocar la función pasada. Puede ser algo así function compose(f, g) {return function h(x) {return f(g(x));};}, que es puro a pesar de tomar las funciones como argumentos.
user2357112 es compatible con Monica el
1
"Me resulta raro que las funciones impuras se utilicen como argumentos de función para funciones puras". - no es un lenguaje funcional, pero ciertas funciones de la biblioteca C ++ tienen advertencias específicas de que los argumentos predicados deben ser (alguna aproximación a) puros. Entonces, en cierto sentido, esto significa que no solo es raro, sino que nunca sucede de manera válida. Pero en otro sentido, el hecho de que tienen que prohibirlo es porque las personas a veces quieren hacerlo. Por ejemplo, quieren pasar una findrutina a un predicado impuro que devuelve "verdadero" para el tercer elemento coincidente que encuentra, o alguna tontería.
Steve Jessop
19

Dado que la pureza de un parámetro de entrada es desconocida hasta el tiempo de ejecución, ¿se considera una función inmediatamente impura si toma una función como parámetro de entrada?

No. Contraejemplo:

function pure(other_function) {
    return 1;
}

No importa si other_functiones una función pura, una función impura o no es una función en absoluto. La purefunción es pura.

Otro contraejemplo:

function identity(x) {
    return x;
}

Esta función es pura, incluso si xes una función impura. identity(impure_function)siempre regresará impure_function, sin importar cuántas veces repita la llamada. No importa si identity(impure_function)()siempre devuelve lo mismo; El valor de retorno de una función no afecta su pureza.


En general, si una función puede llamar a una función, se pasó como argumento, no es pura. Por ejemplo, una función function call(f) {f();}no es pura, porque a pesar de que no menciona ningún estado global o mutable, fpodría ser algo así alertque cause efectos secundarios visibles.

Si una función toma funciones como argumentos, pero no las llama ni hace que se llamen, entonces puede ser pura. Todavía podría ser impuro si hace alguna otra cosa impura. Por ejemplo, function f(ignored_function) {alert('This isn't pure.');}es impuro, aunque nunca llame ignored_function.

user2357112 es compatible con Monica
fuente
44
Esta respuesta parece demasiado pedante. Podemos deducir de la pregunta que la preocupación es sobre el parámetro de función que se invoca. La existencia de funciones que pueden / toman otras funciones como un parámetro sin invocarlas no afecta esta pregunta.
walpen
13
@walpen: La pregunta no menciona la invocación del argumento. No hay razón para suponer que el interrogador se dio cuenta de que una función podría tomar otra función como entrada sin invocarla. Es importante señalar supuestos ocultos como este, en lugar de suponer que se supone que debe asumirlos.
user2357112 es compatible con Monica el
12

Dado que la pureza de un parámetro de entrada es desconocida hasta el tiempo de ejecución, ¿se considera una función inmediatamente impura si toma una función como parámetro de entrada?

Técnicamente, sí, a menos que haya alguna forma en su idioma para garantizar que la función de entrada también sea pura.

si una función aplica una función pura que se define fuera de la función, pero no se pasa como parámetro, ¿sigue siendo pura si cumple los criterios de no tener efectos secundarios y la salida depende únicamente de la entrada?

Si. Así que centrémonos en lo que importa aquí. Llamar a una función pura o no no es en sí misma útil. Las funciones puras son útiles porque producir el mismo resultado para cualquier entrada y no depender del estado o tener efectos secundarios es un conjunto de propiedades muy útil. Significa que una vez que se ha ejecutado su función, puede "recordar" la respuesta para esa entrada y siempre será verdadera. Tampoco necesita ejecutar la función nuevamente para generar efectos secundarios. Y puede ejecutar esa función en paralelo (o fuera de orden) con otras funciones y saber que no tendrán interacciones ocultas que se comporten mal.

Esas propiedades útiles aún se mantienen si la función usa otras funciones de solo lectura para hacer su trabajo, independientemente de cómo las haga referencia.

Telastyn
fuente
5

Como dijo Telastyn: Técnicamente, sí, a menos que haya alguna forma en su idioma para garantizar que la función de entrada también sea pura.

Eso no es hipotético, de hecho, hay buenas maneras de garantizar esto. Al menos en un lenguaje fuertemente tipado.

Una función ~ tan pura que escribirías en JavaScript como

function foo(f) {
   return f(1) + 2;
}

se puede traducir directamente a Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Ahora, en JavaScript puedes hacer cosas malas como

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

Esto no es posible en Haskell . La razón es que algo como efectos secundarios console.log()siempre debe tener un tipo de resultado IO something, no somethingsolo.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

Para que esta expresión escriba typecheck, necesitaríamos dar foola firma de tipo

foo :: (Int -> IO Int) -> Int

Pero resulta que ya no puedo implementarlo más: porque la función de argumento tiene IOsu resultado, no puedo usarlo dentro foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

La única forma en que podría usar una IOacción fooes si el resultado de footiene el tipo en IO Intsí mismo:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

Pero en este punto, queda claro por la firma de fooque tampoco es una función pura.

a la izquierda
fuente
1
Antes de decir "imposible", eche un vistazo a unsafeIO:-)
Bergi
2
@Bergi: eso en realidad no es parte de Haskell, sino de su interfaz de funciones foráneas: para permitir afirmar que una función definida en otro idioma es pura, lo que el compilador de Haskell obviamente no puede inferir de la firma de tipo porque otros idiomas generalmente no tienen tal cosa como IO. Por cierto, también se puede usar para causar caos al ocultar los efectos secundarios en una función "pura", pero eso es realmente inseguro en Haskell porque no hay una forma confiable de especificar el orden de evaluación de las funciones puras.
Leftaroundabout
Si eso es verdad. Sin embargo, creo que también he visto que se usa para "ocultar" efectos secundarios beneficiosos en una función "pura", donde antes era necesario establecer la seguridad del enfoque.
Bergi
@Bergi En su mayoría no deberías estar usando unsafeIO; esa es una escotilla de escape de último recurso que elude el sistema de tipos garantizado y, por lo tanto, el suyo no es un buen punto.
Andres F.
0

No, no es.

Si la función aprobada es impura Y su función llama a la función aprobada, entonces su función se considerará impura.

La relación pura / impura es un poco como sync / async en JS. Puede usar libremente código puro de impuro, pero no al revés.

Bobby Marinoff
fuente
Esta respuesta no agrega nada que no haya sido explicado en esta. ... Por favor, revise las respuestas anteriores antes de reajustar las cosas :)
Andres F.
¿Qué pasa con la analogía de sincronización / asíncrono?
Bobby Marinoff