Como os futuros e as promessas do Clojure diferem?

86

Tanto os futuros quanto as promessas se bloqueiam até que tenham calculado seus valores, então qual é a diferença entre eles?

appshare.co
fonte
8
Não sei por que o -1 na pergunta, ou são perguntas em que você não sabe a resposta antes de perguntar agora coisas ruins?
APENAS MINHA OPINIÃO correta
Eu não -1 nenhuma das respostas ?? Como podemos saber quem colocou -1 em uma pergunta ou resposta?
appshare.co
Você e eu não podemos, Zubair. Estou apenas curioso para saber quem colocou -1 em sua pergunta, visto que é uma pergunta perfeitamente razoável de se fazer e está definitivamente no assunto para SO.
APENAS MINHA OPINIÃO correta

Respostas:

53

Respondendo em termos de Clojure, aqui estão alguns exemplos do screencast de Sean Devlin :

(def a-promise (promise))
(deliver a-promise :fred)

(def f (future (some-sexp)))
(deref f)

Observe que, na promessa, você está entregando explicitamente um valor que selecionou em um cálculo posterior ( :fredneste caso). O futuro, por outro lado, está sendo consumido no mesmo lugar em que foi criado. O some-expré presumivelmente lançado nos bastidores e calculado em conjunto (eventualmente), mas se permanecer sem avaliação no momento em que for acessado, o thread bloqueia até que esteja disponível.


editado para adicionar

Para ajudar a distinguir ainda mais entre uma promessa e um futuro, observe o seguinte:

promessa

  1. Você cria um promise. Esse objeto de promessa agora pode ser passado para qualquer thread.
  2. Você continua com os cálculos. Estes podem ser cálculos muito complicados envolvendo efeitos colaterais, download de dados, entrada do usuário, acesso ao banco de dados, outras promessas - o que você quiser. O código será muito parecido com o código da linha principal em qualquer programa.
  3. Quando terminar, você pode deliverenviar os resultados para esse objeto de promessa.
  4. Qualquer item que tente derefcumprir sua promessa antes de terminar o cálculo será bloqueado até que você termine. Depois de terminar e delivercumprir a promessa, a promessa não bloqueará mais.

futuro

  1. Você cria seu futuro. Parte do seu futuro é uma expressão para cálculo.
  2. O futuro pode ou não ser executado simultaneamente. Ele pode ser atribuído a um thread, possivelmente de um pool. Ele poderia apenas esperar e não fazer nada. De sua perspectiva, você não pode dizer .
  3. Em algum ponto, você (ou outro tópico) derefé o futuro. Se o cálculo já foi concluído, você obtém os resultados dele. Se ainda não foi concluído, você bloqueia até que seja concluído. (Provavelmente, se ainda não começou, derefsignifica que começou a ser executado, mas isso também não é garantido.)

Embora você possa tornar a expressão no futuro tão complicada quanto o código que segue a criação de uma promessa, é duvidoso que isso seja desejável. Isso significa que os futuros são realmente mais adequados para cálculos rápidos e em segundo plano, enquanto as promessas são realmente mais adequadas para caminhos de execução grandes e complicados. Também, as promessas parecem, em termos de cálculos disponíveis, um pouco mais flexíveis e orientadas para o criador da promessa fazendo o trabalho e outro fio colhendo a colheita. Os futuros são mais orientados para iniciar automaticamente um thread (sem a sobrecarga feia e propensa a erros) e continuar com outras coisas até que você - o thread de origem - precise dos resultados.

APENAS MINHA OPINIÃO correta
fonte
Mas você pode ter qualquer bloco de cálculo até que uma promessa OU um futuro seja concluído. ou seja: (@a + @b) funcionará da mesma forma com um futuro e uma promessa
appshare.co
2
Uma promessa permite mais flexibilidade que parece para mim. Eu crio uma promessa. Eu passo essa promessa para outro tópico. Posso então fazer muitos cálculos complicados, incluindo esperar por E / S, baixar dados da Internet, esperar a entrada do usuário, etc. Quando estiver tudo feito, eu cumpro a promessa com o valor resultante. Um futuro cobre uma expressão S. Poderia, eu acho, ser uma expressão S muito, muito complicada, mas seria um pouco ... rígida aí. Além disso, um futuro faz seu trabalho automaticamente em uma thread (ou pool). Fazer o mesmo em uma promessa significaria ainda mais trabalho.
APENAS MINHA OPINIÃO correta
FWIW, uma vez que uma única expressão s pode ser uma chamada para um caminho de código arbitrário, não se trata necessariamente de quanto código você pode inserir na expressão. Então, em vez de dizer que uma promessa é "mais flexível", eu diria que seu propósito é simplesmente diferente. Caso contrário, por que ambos?
Geoff
2
Apenas para registro, o corpo de uma futurechamada pode incluir N sexprs.
vemv
Esta explicação deve ser parte do Clojure Doc
Piyush Katariya
24

Futuro e Promessa são mecanismos para comunicar o resultado da computação assíncrona do produtor para o (s) consumidor (es).

No caso de Future, o cálculo é definido no momento da criação do Future e a execução assíncrona começa "ASAP". Ele também "sabe" como gerar uma computação assíncrona.

No caso do Promise, o cálculo , sua hora de início e [possível] chamada assíncrona são desacoplados do mecanismo de entrega. Quando o resultado do cálculo está disponível, o Produtor deve chamar deliverexplicitamente, o que também significa que o Produtor controla quando o resultado fica disponível.

For Promises Clojure comete um erro de design ao usar o mesmo objeto (resultado da promisechamada) para produzir ( deliver) e consumir ( deref) o resultado da computação . Esses são dois recursos muito distintos e devem ser tratados como tal.

Dimagog
fonte
@oskarkv Suponha que você criou uma promessa e a deu a 3 clientes. Nada impede que um dos clientes resolva o problema com resultados falsos e sinalize para outros dois clientes. Além disso, você não poderá mais cumprir essa promessa. Em contraste, se você tivesse um par promessa + resolvedor, prometesse aos seus clientes e mantivesse o resolvedor para você mesmo, esse cenário se tornaria impossível. Para mais informações, os termos de pesquisa sugeridos são "controle de acesso baseado em capacidade" e "segurança baseada em capacidade".
Dimagog
1
Não tenho certeza se o acoplamento de segurança a um tipo de referência tão simples (verifique seu impl) promiseseria conveniente. Consumidores 'maus' são raros; nada o impede de construir sua própria abstração em cima de promessas.
vemv
8
Não se trata de segurança, mas a programação baseada em capacidade é frequentemente descrita em relação à segurança. Aqui, tudo se resume à correção do código. O termo freqüentemente usado é "correto por construção" e a pergunta é "você pode construir um programa incorreto"? Não de propósito, mas por acidente. Com um único objeto Promise você pode, enquanto com dois objetos separados você não pode.
Dimagog
Não há nada que impeça você de retornar uma promessa que não pode ser cumprida, se é isso que você quer: (defn undeliverable-promise [] (let [p (promise)] (reify clojure.lang.IDeref (deref [_] (deref p)) clojure.lang.IBlockingDeref (deref [_ ms val] (deref p ms val)) clojure.lang.IPending (isRealized [_] (.isRealized p)) clojure.lang.IFn (invoke [_ _] nil))))
tapichu
Apontar a diferença de como o mecanismo de computação é desacoplado tornou este post uma explicação realmente concisa. Obrigado!
synthomat
3

Já existem respostas excelentes, então apenas adicionando o resumo "como usar":

Ambos

Criar promessa ou futuro retorna uma referência imediatamente. Esta referência bloqueia em @ / deref até que o resultado do cálculo seja fornecido por outro thread.

Futuro

Ao criar o futuro, você fornece um trabalho síncrono a ser feito. É executado em um thread do pool ilimitado dedicado.

Promessa

Você não dá argumentos ao criar promessa. A referência deve ser passada para outro encadeamento de 'usuário' que será delivero resultado.

Grzegorz Luczywo
fonte
1

Em Clojure, promise, future, e delaysão promessa semelhante objetos. Todos eles representam uma computação que os clientes podem esperar usando deref(ou @). Os clientes reutilizam o resultado, para que o cálculo não seja executado várias vezes.

Eles diferem na forma como o cálculo é realizado:

  • futureirá iniciar o cálculo em um thread de trabalho diferente. derefirá bloquear até que o resultado esteja pronto.

  • delayexecutará o cálculo preguiçosamente, quando o primeiro cliente usar deref, ou force.

  • promiseoferece mais flexibilidade, pois seu resultado é fornecido de qualquer forma personalizada usando deliver. Você o usa quando nenhum futureou delaycorresponde ao seu caso de uso.

Ernesto
fonte
-4

Em primeiro lugar, a Promiseé a Future. Acho que você quer saber a diferença entre a Promisee a FutureTask.

A Futurerepresenta um valor que não é conhecido atualmente, mas será conhecido no futuro.

A FutureTaskrepresenta o resultado de um cálculo que acontecerá no futuro (talvez em algum pool de threads). Quando você tenta acessar o resultado, se o cálculo ainda não aconteceu, ele bloqueia. Caso contrário, o resultado é retornado imediatamente. Não há nenhuma outra parte envolvida no cálculo do resultado, pois o cálculo é especificado por você antecipadamente.

A Promiserepresenta um resultado que será entregue pelo prometente ao prometido no futuro. Nesse caso, você é o prometido e o prometedor é aquele que lhe deu o Promiseobjeto. Semelhante ao FutureTask, se você tentar acessar o resultado antes de Promiseter sido cumprido, ele será bloqueado até que o prometente cumpra o Promise. Assim que o Promisefor cumprido, você obterá o mesmo valor sempre e imediatamente. Ao contrário de um FutureTask, há uma outra parte envolvida aqui, aquela que fez o Promise. Essa outra parte é responsável por fazer o cálculo e cumprir o Promise.

Nesse sentido, a FutureTaské um que Promisevocê fez para si mesmo.

Abhinav Sarkar
fonte
Você tem certeza de que uma promessa é um futuro? não consigo descobrir que ele implementa a interface. github.com/richhickey/clojure/blob/…
Mikael Sundberg
desculpe, eu pressionei errado entrar. Minha pergunta foi alterada
Mikael Sundberg
Minha resposta é em um sentido geral, não em um sentido específico de Clojure.
Abhinav Sarkar
9
Responder a uma pergunta sobre Clojure com código Java parece um pouco extravagante.
APENAS MINHA OPINIÃO correta