f# functional-programming currying higher-order-functions

f# - Currying inversa?



functional-programming higher-order-functions (3)

Eso sería bastante inteligente ...

Tan astuto que acabaría con el sistema de tipos. Lo que desea es la programación de arreglos como en APL.

¿Hay alguna manera en que pueda (casi) hacer eso?

No hablo F #, pero en Haskell, desatascaría F1 , luego escribiría con *10 , luego curry:

f2 = curry ((*10) . uncurry f1)

Que en un dialecto ML como F # se convierte en algo así como:

let curry f x y = f (x,y) let uncurry f (x,y) = f x y let mult x y = x * y let F1 x y = x + y let F2 = curry (uncurry F1 >> mult 10)

(No estaba seguro de si curry y uncurry están en la biblioteca estándar F #, entonces los uncurry . También puede haber una forma más bonita de hacer una aplicación parcial de * sin definir mult .)

Me gustaría componer funciones de cierta manera. Considere estas 2 funciones en pseudocódigo (no F #)

F1 = x + y F2 = F1 * 10 // note I did not specify arguments for F1, ''reverse curry'' for lack of a better word

Lo que me gustaría que F # hiciera es descubrir que desde

let F1 x y = x + y //val F1 : int -> int -> int

el código let F2 = F1 * 10 me diera la misma firma que F1: val F2 : int -> int -> int , y llamar a F2 2 3 resultaría en 50: (2 + 3) * 10. Eso sería más bien inteligente...

Lo que sucede es bastante diferente aunque La primera línea sale como se espera:

let F1 x y = x + y //val F1 : int -> int -> int

pero cuando agrego una segunda línea, let F2 = F1 * 10 arroje F #. Se queja de que the type int does not match the type ''a -> ''b -> ''c y que F1 ahora requires member ( + ) .

Por supuesto, podría deletrearlo así:

let F1(x, y) = x + y let F2(x, y) = F1(x, y) * 10

Pero ahora bien podría haber usado C #, ya no estamos tan lejos. Los argumentos agrupados rompen la elegancia de F #. También mis funciones reales F1 y F2 tienen muchos más argumentos que solo 2, así que esto me hace cruzar los ojos, exactamente lo que quería esquivar usando F #. Decirlo así sería mucho más natural:

let F1 x y = x + y let F2 = F1 * 10

¿Hay alguna manera en que pueda (casi) hacer eso?

Para créditos adicionales: ¿qué ocurre exactamente con estos mensajes de error? ¿Por qué la segunda línea let F2 = F1 * 10 cambie el tipeo en el primero?

Gracias de antemano por sus pensamientos,

Gert-Jan

actualización Dos apporaches que (casi) hacen lo que se describe.

Uno usando una tupla. La segunda línea parece un poco peculiar primero, funciona bien. El pequeño inconveniente es que no puedo usar currying ahora o tendré que agregar aún más código extravagante.

let F1 (a, b) = a + b let F2 = F1 >> (*) 10 F2(2, 3) // returns 50

Otro enfoque es usar un registro. Eso es un poco más directo y más fácil de obtener a primera vista, pero requiere más código y ceremonia. Elimina parte de la elegancia de F #, se parece más a C #.

type Arg (a, b) = member this.A = a member this.B = b let F1 (a:Arg) = a.A + a.B let F2 (a:Arg) = F1(a) * 10 F2 (Arg(2, 3)) // returns 50


No hay un patrón para esto en general. El uso de combinators (como curry y uncurry ) como lo sugiere larsmans es una opción, pero creo que el resultado es menos legible y más largo que la versión explícita.

Si utiliza este patrón en particular a menudo, puede definir un operador para multiplicar una función (con dos parámetros) por un escalar:

let ( ** ) f x = fun a b -> (f a b) * x let F1 x y = x + y let F2 = F1 ** 10

Desafortunadamente, no puede agregar la implementación de operadores numéricos estándar ( * , etc.) a tipos existentes (como ''a -> ''b -> int ). Sin embargo, esta es una solicitud bastante frecuente (y sería útil para otras cosas). Alternativamente, podría envolver la función en algún objeto que proporcione operadores numéricos sobrecargados (y contenga algún método Invoke para ejecutar la función).

Creo que se eliminaría un nombre apropiado para esto: está elevando el operador * (trabajando en enteros) a una versión que funciona en funciones que devuelven enteros. Es similar a la elevación que se realiza en el compilador C # cuando usa * para trabajar con tipos que aceptan nulos.

Para explicar el mensaje de error: se queja de la expresión F1 * 10 :

error FS0001: el tipo ''int'' no coincide con el tipo '''' a -> ''b ->'' c ''

Creo que significa que el compilador está intentando encontrar una instanciación para el operador * . Desde el lado derecho, se da cuenta de que esto debe ser int , por lo que piensa que el lado izquierdo también debe ser int , pero en realidad es una función de dos argumentos, algo así como ''a -> ''b -> c'' .


Por cierto, usando el enfoque sin puntos (o más bien sin sentido en este caso) uno podría definir estas funciones de la siguiente manera:

let F1 = (+) let F2 = (<<)((*)10) << F1