Un lenguaje basado en la cantidad limitada de argumentos pasados ​​a funciones

16

La idea está inspirada en el hecho de que operadores como +, -,%, etc. se pueden ver como funciones con uno o dos argumentos aprobados y sin efectos secundarios. Suponiendo que yo, u otra persona, escriba un lenguaje que impida que se pasen más de dos argumentos, y que también solo funcione a través del valor de retorno:

a) ¿tal lenguaje conduciría a un código más fácil de entender?

b) ¿sería más claro el flujo del código? (forzado a más pasos, con potencialmente menos interacciones 'ocultas'

c) las restricciones harían que el lenguaje fuera excesivamente voluminoso para programas más complejos.

d) (bonus) cualquier otro comentario sobre pros / contras

Nota:

Todavía tendrían que tomarse dos decisiones: la primera es si se debe permitir la entrada del usuario fuera de main () o su equivalente, y también cuál será la regla con respecto a lo que sucede al pasar matrices / estructuras. Por ejemplo, si alguien quiere que una sola función agregue múltiples valores, podría sortear la limitación al agruparla en una matriz. Esto podría detenerse al no permitir que una matriz o estructura interactúe consigo misma, lo que aún le permitiría, por ejemplo, dividir cada número por una cantidad diferente, dependiendo de su posición.


fuente
44
Hola. Las listas de pros y contras tienden a dar malas respuestas. ¿Hay alguna forma de reformular su pregunta para obtener la información que necesita pero en otro formato?
MetaFight
22
Tu razonamiento ni siquiera comienza a tener sentido para mí. Algunas funciones tienen pocos argumentos, así que limitemos todas las funciones. Normalmente cuando uno propone restricciones arbitrarias, hay una razón, algo que ganar. No puedo ver lo que esto podría ganar.
2
No es que haya algo intrínsecamente incorrecto con las preguntas 'qué pasaría si' (aunque a veces son difíciles de responder como dijo @MetaFight), pero si incluso usted, que pensó en eso y se preocupó lo suficiente como para hacer una pregunta, realmente no puede nombrar un beneficio, entonces estoy bastante seguro de que mi reacción inicial de "¡qué? ¡no! Eso es estúpido, ¿por qué harías eso?" es correcta.
66
Hay bastantes idiomas que solo permiten un argumento por función: cualquier cosa basada en el cálculo lambda. El resultado general es una función que toma un argumento simple lista, o una función que devuelve una función que toma el siguiente argumento hasta que todos los argumentos han sido procesados: result = f(a)(b)…(z). Este es el caso en la familia de idiomas de ML como Haskell, pero también conceptualmente en otros idiomas como Lisp, JavaScript o Perl.
amon
3
@Orangesandlemons: Bien, entonces puedo codificar un número arbitrario de enteros dentro de un solo entero usando solo multiplicación y suma (para codificar) y división y resta (para decodificar). Por lo tanto, también debe rechazar los enteros, o al menos la multiplicación, suma, división y resta. (Una consecuencia del poder de la programación es que puede codificar casi cualquier cosa usando casi cualquier cosa, y por lo tanto restringir las cosas es muy, muy difícil. En general, las restricciones en realidad no "restringen" nada, solo molestan a los programadores.)
Jörg W Mittag

Respuestas:

40

Robert C. Martin en su libro "Clean Code" recomienda en gran medida el uso de funciones con 0, 1 o 2 parámetros como máximo, por lo que al menos hay un autor de libros experimentado que piensa que el código se vuelve más limpio al usar este estilo (sin embargo, él es seguramente no es la autoridad definitiva aquí, y sus opiniones son discutibles).

Donde Bob Martin es IMHO correcto es: las funciones con 3 o más parámetros son a menudo indicadores de un olor a código. En muchos casos, los parámetros pueden agruparse para formar un tipo de datos combinado, en otros casos, puede ser un indicador de que la función simplemente hace demasiado.

Sin embargo, no creo que sea una buena idea inventar un nuevo lenguaje para esto:

  • Si realmente desea aplicar una regla de este tipo en todo el código, solo necesita una herramienta de análisis de código para un lenguaje existente, no es necesario inventar un lenguaje completamente nuevo para esto (por ejemplo, para C # algo como 'fxcop' probablemente podría utilizarse )

  • a veces, combinar parámetros con un nuevo tipo simplemente no parece la pena, o se convertiría en una combinación artificial pura. Vea, por ejemplo, este File.Openmétodo del marco .Net. Se necesitan cuatro parámetros, y estoy bastante seguro de que los diseñadores de esa API lo hicieron intencionalmente, porque pensaron que sería la forma más práctica de proporcionar los diferentes parámetros a la función.

  • a veces hay escenarios del mundo real en los que más de 2 parámetros simplifican las cosas por razones técnicas (por ejemplo, cuando necesita una asignación 1: 1 a una API existente en la que está obligado al uso de tipos de datos simples y no puede combinar diferentes parámetros en un objeto personalizado)

Doc Brown
fuente
16
El olor con múltiples parámetros es a menudo los diferentes parámetros que realmente van de la mano. Tomemos, por ejemplo, el cálculo del índice de masa corporal, IMC. Es una función de la longitud y el peso de una persona. f (longitud, peso), pero esos dos parámetros realmente se unen porque ¿alguna vez harías este cálculo con la altura de una persona y el peso de otra? Entonces, para representar eso mejor, obtendría f (persona) donde la persona puede tener una interfaz de peso y longitud.
Pieter B
@PieterB: por supuesto, mira mi edición.
Doc Brown
55
Minúsculo lenguaje diminuto : "podría indicar un olor a código" ¿No es un olor a código, por definición, solo una indicación de que debería reconsiderar algo, incluso si finalmente no cambia el código? Entonces, un olor a código no se "indicaría". Si un aspecto específico indica la posibilidad de un problema, es un olor a código. ¿No?
jpmc26
66
@ jpmc26: Desde otro punto de vista, un olor a código es un posible problema, no la indicación de uno ;-) Todo depende de la definición exacta del olor a código (y una vez que huele, se ha vuelto malo, ¿no? ?)
hoffmale
3
@PieterB ¿Alguien realmente hace esto? Ahora debe crear una nueva instancia de Persona cada vez que solo desee calcular un IMC con dos valores arbitrarios. Claro, si su aplicación ya usa Personas para empezar y se encuentra haciendo algo como f (person.length, person.height), podría limpiarlo un poco, pero crear nuevos objetos específicamente para agrupar parámetros parece excesivo.
Abogados el
47

Hay muchos idiomas que ya funcionan de esta manera, por ejemplo, Haskell. En Haskell, cada función toma exactamente un argumento y devuelve exactamente un valor.

Siempre es posible reemplazar una función que toma n argumentos con una función que toma n-1 argumentos y devuelve una función que toma el argumento final. Aplicando esto de forma recursiva, siempre es posible reemplazar una función que toma un número arbitrario de argumentos con una función que toma exactamente un argumento. Y esta transformación puede realizarse mecánicamente, mediante un algoritmo.

Esto se llama Frege-Schönfinkeling, Schönfinkeling, Schönfinkel-Currying, o Currying, después de Haskell Curry, quien lo investigó extensamente en la década de 1950, Moses Schönfinkel, que lo describió en 1924, y Gottlob Frege, que lo presagió en 1893.

En otras palabras, restringir el número de argumentos tiene exactamente cero impacto.

Jörg W Mittag
fuente
2
Es decir, si permite que se devuelvan funciones, por supuesto. Incluso si no, todo lo que tiene que hacer es tener la siguiente función llamada en el programa principal. Es decir, podría tener 1 + 1 + 1 donde la primera suma es una función que devuelve una función para una suma adicional, o simplemente se puede llamar dos veces. El último estilo sería, en teoría, más limpio, ¿o me equivoco?
55
"tiene exactamente cero impacto": la pregunta del OP era si la capacidad de lectura del código aumentaría o disminuiría, y supongo que usted no afirma que tal decisión de diseño para un idioma no tenga impacto en eso, ¿verdad?
Doc Brown
3
@DocBrown Con un poder decente y una sobrecarga del operador, puedo tomar un lenguaje curry y hacer que parezca un lenguaje no curioso. Ejemplo: f *(call_with: a,b,c,d,e) Sobrecarga call_with :para comenzar una cadena, ,para extender la cadena y *en el LHS para invocar fpasando cada uno de los contenidos de la cadena uno a la vez. Un sistema de sobrecarga del operador suficientemente débil solo hace que la sintaxis sea engorrosa, pero esa es la falla del sistema de sobrecarga del operador más que nada.
Yakk
En realidad, el curry de Haskell reduce una función con n argumentos a una función con un argumento que devuelve otra función con n - 1 argumentos.
Ryan Reich
2
@RyanReich Tiene razón en que puede ver un "fantasma" del "número de argumentos" de una función Haskell en su firma de tipo. Es un fantasma en lugar de una firma verdadera porque, en general, no tiene forma de saber si la última variable de tipo en la firma también es un tipo de función. En cualquier caso, este fantasma no invalida el hecho de que en Haskell todas las funciones toman 1 argumento y devuelven un valor que no es una función u otra función que también toma 1 argumento. Esto está integrado en la asociatividad de ->: a-> b-> c es a -> (b-> c). Así que estás más equivocado aquí.
Ian
7

He pasado algún tiempo estas últimas semanas intentando aprender el lenguaje informático J. En J, casi todo es un operador, por lo que solo obtienes "mónadas" (funciones que tienen un solo argumento) y "díadas" (funciones con exactamente dos argumentos). Si necesita más argumentos, debe proporcionarlos en una matriz o proporcionarlos en "cuadros".

J puede ser muy conciso, pero al igual que su predecesor APL, también puede ser muy críptico, pero esto es principalmente el resultado del objetivo del creador de emular la concisión matemática. Es posible hacer que un programa J sea más legible utilizando nombres en lugar de caracteres para crear operadores.

Alfeo
fuente
ah, entonces permite que las matrices interactúen con ellas mismas.
5

Un lenguaje basado en cómo restringe al desarrollador depende de la suposición de que el desarrollador del lenguaje comprende las necesidades de cada programador mejor de lo que el programador entiende esas necesidades por sí mismo. Hay casos en que esto es realmente válido. Por ejemplo, muchos consideran que las restricciones en la programación multiproceso que requieren sincronización mediante mutexes y semáforos son "buenas" porque la mayoría de los programadores desconocen por completo las complejidades subyacentes específicas de la máquina que esas restricciones les ocultan. Del mismo modo, pocos desean comprender completamente los matices de los algoritmos de recolección de basura multiproceso; Se prefiere un lenguaje que simplemente no le permita romper el algoritmo GC sobre uno que obligue a un programador a ser consciente de demasiados matices.

Tendría que hacer un argumento válido de por qué, como desarrollador de lenguaje, entiende que el argumento pasa mucho mejor que los programadores que usan su lenguaje que tiene valor evitar que hagan cosas que considera perjudiciales. Creo que sería un argumento difícil de hacer.

También debe saber que los programadores evitarán sus limitaciones. Si necesitan 3 o más argumentos, utilizarán técnicas como curry para convertirlos en llamadas de menos argumentos. Sin embargo, esto a menudo conlleva el costo de la legibilidad, en lugar de mejorarlo.

La mayoría de los idiomas que conozco con este tipo de regla son esolangs, lenguajes diseñados para demostrar que puedes operar con un conjunto limitado de funcionalidades. En particular, los esolangs donde cada personaje es un código de operación tienen una tendencia a limitar el número de argumentos, simplemente porque necesitan mantener corta la lista de códigos de operación.

Cort Ammon - Restablece a Monica
fuente
Esta es la mejor respuesta.
Jared Smith
1

Necesitarás dos cosas:

  • Cierre
  • Tipo de datos compuesto

Agregaré un ejemplo matemático para explicar la respuesta escrita por Jörg W Mittag .

Considera el función gaussiana .

Una función gaussiana tiene dos parámetros para su forma, a saber, la media (posición central de la curva) y la varianza (relacionada con el ancho de pulso de la curva). Además de los dos parámetros, uno también debe proporcionar el valor de la variable libre xpara evaluarla.

En el primer paso, diseñaremos una función gaussiana que tome los tres parámetros, a saber, la media, la varianza y la variable libre.

En el segundo paso, creamos un tipo de datos compuesto que combina la media y la varianza en una sola cosa.

En el tercer paso, creamos una parametrización de la función gaussiana creando un cierre de la función gaussiana vinculada al tipo de datos compuestos que creamos en el segundo paso.

Finalmente, evaluamos el cierre creado en el tercer paso al pasarle el valor de la variable libre x.

La estructura es por lo tanto:

  • Evaluar (computación)
    • ParameterizedGaussian (cierre: la fórmula, más algunas variables ligadas)
      • GaussianParameters (tipo de datos compuesto)
        • Media (valor)
        • Varianza (valor)
    • X (el valor de la variable libre)
rwong
fuente
1
  1. En casi cualquier lenguaje de programación, puede pasar algún tipo de lista, matriz, tupla, registro u objeto como único argumento. Su único propósito es mantener otros elementos en lugar de pasarlos a una función individualmente. Algunos IDE de Java incluso tienen una función de " Objeto de parámetro de extracción " para hacer precisamente eso. Internamente, Java implementa números variables de argumentos al crear y pasar una matriz.

  2. Si realmente quieres hacer de lo que estás hablando en la forma más pura, debes mirar el cálculo lambda. Es exactamente lo que describe. Puede buscarlo en la web, pero la descripción que tenía sentido para mí estaba en Tipos y lenguajes de programación .

  3. Mire los lenguajes de programación Haskell y ML (ML es más simple). Ambos se basan en el cálculo lambda y, conceptualmente, solo tienen un parámetro por función (si entrecierra un poco).

  4. El ítem 2 de Josh Bloch es: "Considere un constructor cuando se enfrenta con muchos parámetros de constructor". Puede ver cuán detallado es esto , pero es una delicia trabajar con una API escrita de esta manera.

  5. Algunos idiomas han nombrado parámetros, que es otro enfoque para hacer que las firmas de métodos enormes sean mucho más fáciles de navegar. Kotlin ha nombrado argumentos por ejemplo.

GlenPeterson
fuente