Uma linguagem baseada na quantidade limitada de argumentos passados ​​para funções

16

A idéia é inspirada no fato de que operadores como +, -,% etc. podem ser vistos como funções com um ou dois argumentos passados ​​e sem efeitos colaterais. Supondo que eu, ou outra pessoa, escreva uma linguagem que impeça a passagem de mais de dois argumentos e também funcione apenas via valor de retorno:

a) essa linguagem levaria a um código mais fácil de entender?

b) o fluxo do código seria mais claro? (forçado a mais etapas, com potencialmente menos interações "ocultas"

c) as restrições tornariam o idioma excessivamente volumoso para programas mais complexos.

d) (bônus) quaisquer outros comentários sobre prós / contras

Nota:

Duas decisões ainda precisam ser tomadas - a primeira é permitir a entrada do usuário fora de main () ou equivalente, e também qual será a regra em relação ao que acontece ao passar matrizes / estruturas. Por exemplo, se alguém quiser que uma única função adicione vários valores, ele poderá contornar a limitação agrupando-a em uma matriz. Isso pode ser interrompido não permitindo que uma matriz ou estrutura interaja consigo mesma, o que ainda permitiria, por exemplo, dividir cada número por uma quantidade diferente, dependendo da posição.


fonte
4
Oi. As listas de prós e contras tendem a dar respostas ruins. Existe alguma maneira de reformular sua pergunta para obter as informações necessárias, mas em outro formato?
MetaFight
22
Seu raciocínio nem começa a fazer sentido para mim. Algumas funções têm poucos argumentos, então vamos limitar todas as funções? Normalmente, quando se propõe restrições arbitrárias, há uma razão, algo a ser ganho. Não consigo ver o que isso poderia ganhar com você.
2
Não que exista algo inerentemente errado nas perguntas 'e se' (embora às vezes sejam difíceis de responder como o @MetaFight disse), mas se você, que pensou na coisa e se importou o suficiente para fazer uma pergunta, não pode realmente nomear um benefício, então eu tenho certeza que minha reação inicial de "o quê? não! isso é estúpido, por que você faria isso" é precisa.
6
Existem algumas linguagens que permitem apenas um único argumento por função: qualquer coisa com base no cálculo lambda. O resultado geralmente é uma função que toma um único argumento lista, ou uma função que retorna uma função que leva o próximo argumento até que todos os argumentos tenham sido processados: result = f(a)(b)…(z). É o caso da família de idiomas ML, como Haskell, mas também conceitualmente em outros idiomas, como Lisp, JavaScript ou Perl.
amon
3
@ Largesandlemons: Ok, então eu posso codificar um número arbitrário de números inteiros dentro de um único inteiro usando apenas multiplicação e adição (para codificação) e divisão e subtração (para decodificação). Portanto, você também não deve permitir números inteiros, ou pelo menos multiplicação, adição, divisão e subtração. (Uma conseqüência do poder da programação é que você pode codificar quase tudo usando quase tudo, e, portanto, restringir as coisas é realmente muito difícil. Em geral, as restrições não "restringem" nada, apenas irritam os programadores.)
Jörg W Mittag

Respostas:

40

Robert C. Martin em seu livro "Clean Code" recomenda fortemente o uso de funções com 0, 1 ou 2 parâmetros no máximo; portanto, pelo menos há um autor de livro experiente que acha que o código se torna mais limpo usando esse estilo (no entanto, ele é certamente não a autoridade ultimativa aqui, e suas opiniões são discutíveis).

Onde Bob Martin está IMHO correto é: funções com 3 ou mais parâmetros geralmente são indicadores de um cheiro de código. Em muitos casos, os parâmetros podem ser agrupados para formar um tipo de dados combinado; em outros casos, pode ser um indicador para a função simplesmente fazendo muito.

No entanto, não acho que seria uma boa ideia inventar um novo idioma para isso:

  • se você realmente deseja impor essa regra em todo o seu código, precisa apenas de uma ferramenta de análise de código para um idioma existente, não há necessidade de inventar um idioma completamente novo para isso (por exemplo, para C #, algo como 'fxcop' provavelmente poderia ser utilizado )

  • às vezes, combinar parâmetros com um novo tipo simplesmente não parece incomodar, ou isso se tornaria uma combinação artificial pura. Veja, por exemplo, este File.Openmétodo na estrutura .Net. São necessários quatro parâmetros, e tenho certeza de que os projetistas dessa API fizeram isso intencionalmente, porque acharam que seria a maneira mais prática de fornecer os parâmetros diferentes para a função.

  • Às vezes, existem cenários do mundo real em que mais de 2 parâmetros simplificam as coisas por motivos técnicos (por exemplo, quando você precisa de um mapeamento 1: 1 para uma API existente em que está vinculado ao uso de tipos de dados simples e não pode combinar diferentes parâmetros em um objeto personalizado)

Doc Brown
fonte
16
O cheiro com vários parâmetros geralmente é que os diferentes parâmetros realmente pertencem um ao outro. Tomemos, por exemplo, o cálculo do índice de massa corporal, IMC. É uma função do comprimento e peso de uma pessoa. f (comprimento, peso), mas esses dois parâmetros realmente pertencem um ao outro, porque você faria esse cálculo com a altura de uma pessoa e o peso de outra? Então, para representar melhor, você obteria f (pessoa) onde a pessoa pode ter uma interface de peso e comprimento.
Pieter B
@ PieterB: é claro, veja minha edição.
Doc Brown
5
Minúsculo, pequena nitpick língua: "pode indicar um cheiro de código" não é um cheiro de código, por definição, apenas uma indicação de que você deveria reconsiderar algo, mesmo se, em última análise não alterar o código? Portanto, um cheiro de código não seria "indicado". Se um aspecto específico indica a possibilidade de um problema, é um cheiro de código. Não?
Jpmc26
6
@ jpmc26: De outro ponto de vista, um cheiro de código é um possível problema, não a indicação de um ;-) Tudo depende da definição exata de cheiro de código (e, quando cheira, fica ruim, não é? ?)
hoffmale
3
@PieterB Alguém realmente faz isso? Agora você deve criar uma nova instância de Person sempre que quiser calcular um IMC com dois valores arbitrários. Claro, se o seu aplicativo já usa o People e você se encontra fazendo algo como f (person.length, person.height), você pode limpá-lo um pouco, mas criar novos objetos especificamente para agrupar os parâmetros parece um exagero.
Lawyerson
47

Existem muitos idiomas que já funcionam dessa maneira, por exemplo, Haskell. Em Haskell, toda função usa exatamente um argumento e retorna exatamente um valor.

Sempre é possível substituir uma função que aceita n argumentos por uma função que aceita n-1 e retorna uma função que aceita o argumento final. Aplicando isso recursivamente, sempre é possível substituir uma função que recebe um número arbitrário de argumentos por uma função que recebe exatamente um argumento. E essa transformação pode ser realizada mecanicamente, por um algoritmo.

Isso se chama Frege-Schönfinkeling, Schönfinkeling, Schönfinkel-Currying ou Currying, em homenagem a Haskell Curry, que a pesquisou extensivamente na década de 1950, Moses Schönfinkel, que a descreveu em 1924, e Gottlob Frege, que a antecipou em 1893.

Em outras palavras, restringir o número de argumentos tem exatamente zero impacto.

Jörg W Mittag
fonte
2
Isto é, se você permitir que as funções sejam retornadas, é claro. Mesmo se não, tudo o que você precisa fazer é ter a próxima função chamada no programa principal. Ou seja, você poderia ter 1 + 1 + 1 onde a primeira adição é uma função que retorna uma função para adição adicional, ou pode ser simplesmente chamada duas vezes. O último estilo seria, em teoria, mais limpo, ou estou enganado?
5
"tem exatamente zero impacto" - A pergunta do OP era se a legibilidade do código aumentaria ou diminuiria, e acho que você não afirma que uma decisão de design desse tipo para um idioma não tenha impacto nisso, não é?
Doc Brown
3
@DocBrown Com energia decente e sobrecarga do operador, posso usar um idioma curry e fazer com que pareça um idioma sem ocultação. Exemplo: f *(call_with: a,b,c,d,e) Sobrecarga call_with :para iniciar uma cadeia, ,estender a cadeia e, *no LHS, invocar fpassando cada conteúdo da cadeia, um de cada vez. Um sistema de sobrecarga de operador suficientemente fraco apenas torna a sintaxe complicada, mas isso é culpa do sistema de sobrecarga de operador mais do que qualquer coisa.
Yakk
Na verdade, o curry de Haskell reduz uma função com n argumentos para uma função com um argumento retornando outra função com n - 1 argumentos.
Ryan Reich
2
@RyanReich Você está certo ao ver um "fantasma" do "número de argumentos" de uma função Haskell em sua assinatura de tipo. É um fantasma, e não uma assinatura verdadeira, porque em geral você não tem como saber se a última variável de tipo na assinatura também é um tipo de função. De qualquer forma, esse fantasma que está lá não invalida o fato de que, em Haskell, todas as funções recebem 1 argumento e retornam um valor não funcional ou outra função que também recebe 1 argumento. Isso é incorporado à associatividade de ->: a-> b-> c é a -> (b-> c). Então você está errado aqui.
Ian
7

Passei algum tempo nessas últimas semanas tentando aprender a linguagem de computador. Em J, praticamente tudo é um operador, então você só recebe "mônadas" (funções que possuem apenas um argumento) e "díades" (funções com exatamente dois argumentos). Se você precisar de mais argumentos, precisará fornecê-los em uma matriz ou em "caixas".

J pode ser muito conciso, mas, como seu APL predecessor, também pode ser muito enigmático - mas isso é principalmente o resultado do objetivo do criador de imitar a sucessão matemática. É possível tornar um programa J mais legível usando nomes em vez de caracteres para criar operadores.

Alpheus
fonte
ah, portanto, permite que os arrays interajam entre si.
5

Uma linguagem baseada em como restringe o desenvolvedor depende da suposição de que o desenvolvedor da linguagem entende melhor as necessidades de cada programador do que o próprio programador. Há casos em que isso é realmente válido. Por exemplo, muitas restrições na programação multithread que requerem sincronização usando mutexes e semáforos são "boas" porque muitos programadores desconhecem completamente as complexidades subjacentes específicas à máquina que essas restrições ocultam. Da mesma forma, poucos desejam entender completamente as nuances dos algoritmos de coleta de lixo multithread; uma linguagem que simplesmente não permite que você quebre o algoritmo do GC é preferível a uma que força o programador a estar ciente de muitas nuances.

Você teria que apresentar um argumento válido para o motivo pelo qual, como desenvolvedor de linguagem, você entende que a passagem de argumentos é muito melhor do que os programadores que usam sua linguagem, que há valor em impedir que eles façam coisas que considere prejudiciais. Eu acho que seria um argumento difícil de argumentar.

Você também tem que saber que os programadores vão contornar suas limitações. Se eles precisarem de três ou mais argumentos, eles usarão técnicas como currying para transformá-los em chamadas com menos argumentos. No entanto, isso geralmente custa à legibilidade, em vez de melhorá-la.

A maioria das linguagens que conheço com esse tipo de regra são esolangs, linguagens projetadas para demonstrar que você pode realmente operar com um conjunto limitado de funcionalidades. Em particular, os esolangs em que cada caractere é um código de operação tendem a limitar o número de argumentos, simplesmente porque precisam manter a lista de códigos de operação curta.

Cort Ammon - Restabelecer Monica
fonte
Esta é a melhor resposta.
Jared Smith
1

Você precisará de duas coisas:

  • Fecho
  • Tipo de dados compostos

Vou adicionar um exemplo matemático para explicar a resposta escrita por Jörg W Mittag .

Considere a função gaussiana .

Uma função gaussiana tem dois parâmetros para sua forma, a saber, a média (posição central da curva) e a variância (relacionada à largura de pulso da curva). Além dos dois parâmetros, é preciso também fornecer o valor da variável livre xpara avaliá-la.

Na primeira etapa, projetaremos uma função gaussiana que aceita todos os três parâmetros, a média, a variância e a variável livre.

Na segunda etapa, criamos um tipo de dados composto que combina a média e a variação em uma coisa.

Na terceira etapa, criamos uma parametrização da função Gaussiana, criando um fechamento da função Gaussiana ligada ao tipo de dados composto que criamos na segunda etapa.

Por fim, avaliamos o fechamento criado na terceira etapa, passando o valor da variável livre xpara ela.

A estrutura é, portanto:

  • Avaliar (computação)
    • ParameterizedGaussian (encerramento: a fórmula, mais algumas variáveis ​​associadas)
      • GaussianParameters (tipo de dados composto)
        • Média (valor)
        • Variação (valor)
    • X (o valor da variável livre)
rwong
fonte
1
  1. Em praticamente qualquer linguagem de programação, você pode passar algum tipo de lista, matriz, tupla, registro ou objeto como o único argumento. Seu único objetivo é armazenar outros itens em vez de passá-los para uma função individualmente. Alguns IDE Java ainda têm um recurso " Extrair objeto de parâmetro " para fazer exatamente isso. Internamente, o Java implementa números variáveis ​​de argumentos criando e passando uma matriz.

  2. Se você realmente deseja fazer o que está falando da forma mais pura, precisa analisar o cálculo lambda. É exatamente o que você descreve. Você pode pesquisar na web, mas a descrição que fazia sentido para mim estava em Tipos e Linguagens de Programação .

  3. Veja as linguagens de programação Haskell e ML (ML é mais simples). Ambos são baseados no cálculo lambda e conceitualmente têm apenas um parâmetro por função (se você apertar um pouco).

  4. O item 2 de Josh Bloch é: "Considere um construtor quando confrontado com muitos parâmetros de construtor". Você pode ver como isso fica detalhado , mas é uma delícia trabalhar com uma API que é escrita dessa maneira.

  5. Algumas linguagens nomearam parâmetros, que é outra abordagem para facilitar a navegação de grandes assinaturas de métodos. Kotlin nomeou argumentos, por exemplo.

GlenPeterson
fonte