Estou pensando em disponibilizar funções variadas e currying em uma linguagem de programação funcional de tipo dinâmico, mas me pergunto se é possível ou não.
Aqui estão alguns pseudocódigo:
sum = if @args.empty then 0 else @args.head + sum @args.tail
que supostamente soma todos os seus argumentos. Então, se sum
for tratado um número, o resultado será 0
. por exemplo,
sum + 1
é igual a 1, assumindo que +
só possa funcionar em números. No entanto, mesmo sum == 0
é verdade, sum
ainda manterá seu valor e propriedade funcional, independentemente de quantos argumentos sejam fornecidos (portanto, "parcialmente aplicado" e "variável" ao mesmo tempo), por exemplo, se eu declarar
g = sum 1 2 3
então g
é igual a 6
, no entanto, ainda podemos aplicar g
. Por exemplo, g 4 5 == 15
é verdade. Nesse caso, não podemos substituir o objeto g
por um literal 6
, porque, embora eles produzam o mesmo valor quando tratados como um número inteiro, eles contêm códigos diferentes.
Se esse design for usado em uma linguagem de programação real, causará alguma confusão ou ambiguidade?
fonte
sum
está0
sem argumento e se chama recursivamente com um argumento.reduce
?args
:empty
,head
etail
. Essas são todas as funções de lista, sugerindo que talvez a coisa mais fácil e direta a ser feita seria usar uma lista onde as coisas variadas seriam. (Então,sum [1, 2, 3]
em vez desum 1 2 3
)Respostas:
Como as varargs podem ser implementadas? Precisamos de algum mecanismo para sinalizar o fim da lista de argumentos. Isso pode ser
Ambos os mecanismos podem ser usados no contexto de currying para implementar varargs, mas a digitação adequada se torna um problema importante. Vamos supor que estamos lidando com uma função
sum: ...int -> int
, exceto que essa função usa currying (então, na verdade, temos um tipo mais parecidosum: int -> ... -> int -> int
, exceto que não sabemos o número de argumentos).Caso: valor do terminador: Seja
end
o terminador especial eT
seja o tipo desum
. Sabemos agora que a aplicadaend
à função retorna:sum: end -> int
e que aplicado a um int temos outra soma-como função:sum: int -> T
. PortantoT
é a união destes tipos:T = (end -> int) | (int -> T)
. SubstituindoT
, temos vários tipos possíveis, tais comoend -> int
,int -> end -> int
,int -> int -> end -> int
, etc. No entanto, a maioria dos sistemas do tipo não acomodam esses tipos.Caso: comprimento explícito: O primeiro argumento para uma função vararg é o número de varargs. Assim
sum 0 : int
,sum 1 : int -> int
,sum 3 : int -> int -> int -> int
etc. Isto é suportado em alguns sistemas do tipo e é um exemplo de tipagem dependente . Na verdade, o número de argumentos seria um parâmetro de tipo e não um parâmetro regular - não faria sentido para o arity da função de depender de um valor de tempo de execução,s = ((sum (floor (rand 3))) 1) 2
é, obviamente, mal-digitou: Isto avalia como quers = ((sum 0) 1) 2 = (0 1) 2
,s = ((sum 1) 1) 2 = 1 2
ous = ((sum 2) 1) 2 = 3
.Na prática, nenhuma dessas técnicas deve ser usada, pois são propensas a erros e não têm um tipo (significativo) em sistemas de tipos comuns. Em vez disso, apenas passar uma lista de valores como um paramter:
sum: [int] -> int
.Sim, é possível que um objeto apareça como uma função e um valor, por exemplo, em um sistema de tipos com coerção. Let
sum
be aSumObj
, que tem duas coerções:coerce: SumObj -> int -> SumObj
permitesum
ser usado como uma função ecoerce: SumObj -> int
nos permite extrair o resultado.Tecnicamente, essa é uma variação do caso do valor do terminador acima, com
T = SumObj
ecoerce
sendo um desempacotador para o tipo. Em muitas linguagens orientadas a objetos, isso é trivialmente implementável com sobrecarga do operador, por exemplo, C ++:fonte
..., force=False)
para forçar a aplicação da função inicial.curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys)
.Você pode examinar esta implementação do printf em Haskell , juntamente com esta descrição de como ele funciona . Na última página, há um link para o artigo de Oleg Kiselyov sobre esse tipo de coisa, que também vale a pena ler. De fato, se você está criando uma linguagem funcional, o site de Oleg provavelmente deve ser uma leitura obrigatória.
Na minha opinião, essas abordagens são um pouco complicadas, mas mostram que é possível. Se seu idioma possui digitação totalmente dependente, no entanto, é muito mais simples. Uma função variadica para somar seus argumentos inteiros pode ser algo parecido com isto:
Uma abstração para definir o tipo recursivo sem precisar fornecer um nome explícito pode facilitar a gravação dessas funções.
Edit: é claro, acabei de ler a pergunta novamente e você disse uma linguagem de tipo dinâmico , quando obviamente a mecânica do tipo não é realmente relevante e, portanto, a resposta da @ amon provavelmente contém tudo o que você precisa. Bem, vou deixar isso aqui, caso alguém se depare com isso enquanto se pergunta sobre como fazê-lo em uma linguagem estática ...
fonte
Aqui está uma versão para currying de funções variadas no Python3 que usa a abordagem "terminator" do @amon, aproveitando os argumentos opcionais do Python:
A função retornada
f
coleta os argumentos passados a ela em chamadas sucessivas em uma matriz que está vinculada ao escopo externo. Somente quando oforce
argumento é verdadeiro, a função original é chamada com todos os argumentos coletados até o momento.Advertências nesta implementação são que você sempre deve passar um primeiro argumento para
f
que não possa criar um "thunk", uma função em que todos os argumentos estão vinculados e só podem ser chamados com a lista de argumentos vazia (mas acho que isso está alinhado com a implementação típica de curry).Outra ressalva é que, depois de passar um argumento errado (por exemplo, do tipo errado), é necessário refazer a função original. Não há outra maneira de redefinir a matriz interna, isso é feito somente após uma execução bem-sucedida da função ao curry.
Não sei se sua pergunta simplificada, "um objeto pode ser uma função e um valor não funcional ao mesmo tempo?", Pode ser implementada no Python, pois uma referência a uma função sem parênteses é avaliada para o objeto interno da função . Não sei se isso pode ser dobrado para retornar um valor arbitrário.
Provavelmente seria fácil no Lisp, pois os símbolos Lisp podem ter um valor e um valor de função ao mesmo tempo; o valor da função é simplesmente selecionado quando o símbolo aparece na posição da função (como o primeiro elemento em uma lista).
fonte