Uma espécie de questão de novato em programação funcional aqui:
Eu tenho lido as transcrições de algumas das palestras de Rich Hickey e, em várias de suas mais conhecidas, ele recomenda o uso de filas como uma alternativa para que as funções se chamam. (Por exemplo, em design, composição e desempenho e no Simple Made Easy .)
Não entendo bem isso em vários aspectos:
Ele está falando sobre colocar dados em uma fila e depois fazer com que cada função os use? Então, ao invés da função A que chama a função B para realizar seu próprio cálculo, apenas a função B coloca sua saída em uma fila e, em seguida, a função A agarra? Ou, alternativamente, estamos falando sobre colocar funções em uma fila e aplicá-las sucessivamente aos dados (certamente não, porque isso envolveria mutação maciça, certo? E também multiplicação de filas para funções de múltiplas aridades, como árvores ou algo assim? )
Como isso torna as coisas mais simples? Minha intuição seria que essa estratégia criaria mais complexidade, porque a fila seria um tipo de estado, e então você precisa se preocupar "e se alguma outra função aparecer e colocar alguns dados no topo da fila?"
Uma resposta para uma pergunta de implementação no SO sugere que a idéia está criando várias filas diferentes. Portanto, cada função coloca sua saída em sua própria fila (??). Mas isso também me confunde, porque se você estiver executando uma função uma vez, por que ela precisa de uma fila para sua saída, quando você pode apenas pegar essa saída e colocar um nome nela como uma (var, atom, entrada em uma grande tabela de hash, o que for). Por outro lado, se uma função estiver sendo executada várias vezes e você colocar sua saída em uma fila, infligirá um estado a si mesmo novamente e precisará se preocupar com a ordem em que tudo é chamado, as funções downstream ficam menos puras, etc.
Claramente, não estou entendendo o ponto aqui. Alguém pode explicar um pouco?
fonte
Job
objeto genérico , envia-o para uma fila e faz com que um ou mais threads de trabalho funcionem nessa fila. EmJob
seguida, ele envia maisJob
s para a fila após a conclusão. Os valores retornados são substituídos por retornos de chamada nesse conceito. É um pesadelo depurar e verificar se você não possui uma pilha de chamadas, eficiente e flexível pelo mesmo motivo.Respostas:
É mais um exercício de design do que uma recomendação geral. Você não costuma colocar uma fila entre todas as suas chamadas de função diretas. Isso seria ridículo. No entanto, se você não projetar suas funções como se uma fila pudesse ser inserida entre qualquer uma das chamadas diretas de função, não poderá justificadamente afirmar que escreveu código reutilizável e composível. Esse é o argumento de Rich Hickey.
Esta é uma das principais razões por trás do sucesso do Apache Spark , por exemplo. Você escreve um código que parece estar fazendo chamadas diretas de função em coleções locais, e a estrutura converte esse código em passar mensagens nas filas entre nós do cluster. O tipo de estilo de codificação simples, compostável e reutilizável que Rich Hickey defende torna isso possível.
fonte
Uma coisa a observar é que a programação funcional permite conectar funções entre si indiretamente por meio de objetos mediadores que cuidam da aquisição de argumentos para alimentar as funções e rotear de maneira flexível seus resultados para os destinatários que desejam seus resultados. Então, suponha que você tenha algum código de chamada direta simples que se parece com este exemplo, em Haskell:
Bem, usando de Haskell
Applicative
classe e seus<$>
e<*>
operadores podemos mecanicamente reescrever o código para isso:... onde agora
myThing
não está mais chamando diretamentef
eg
, mas conectando-os através de alguns mediadores do tipof
. Assim, por exemplo,f
poderia ser algumStream
tipo fornecido por uma biblioteca que fornece uma interface para um sistema de filas; nesse caso, teríamos esse tipo:Sistemas como este existem. De fato, você pode ver os fluxos do Java 8 como uma versão desse paradigma. Você obtém código como este:
Aqui você está usando as seguintes funções:
t -> t.getType() == Transaction.GROCERY
comparing(Transaction::getValue).reversed()
Transaction::getId
toList()
... e, em vez de fazer com que eles se liguem diretamente, você está usando o
Stream
sistema para mediar entre eles. Este exemplo de código não está chamando aTransaction::getId
função diretamente -Stream
está chamando-a com as transações que sobreviveram à anteriorfilter
. Você pode pensarStream
nisso como um tipo muito mínimo de fila que une funções indiretamente e roteia valores entre elas.fonte