
Muitos hoje amam a programação reativa. Tem muitas vantagens: a falta do chamado " 
inferno de retorno de chamada ", o mecanismo interno de tratamento de erros e um estilo de programação funcional que reduz a probabilidade de erros. Significativamente mais fácil escrever código multiencadeado e mais fácil gerenciar fluxos de dados (combinar, dividir e converter).
Muitas linguagens de programação têm sua própria biblioteca reativa: RxJava para JVM, RxJS para JavaScript, RxSwift para iOS, Rx.NET etc.
Mas o que temos para Kotlin? Seria lógico supor que RxKotlin. E, de fato, existe uma biblioteca desse tipo, mas é apenas um conjunto de extensões para o RxJava2, o chamado "açúcar".
E, idealmente, eu gostaria de ter uma solução que atenda aos seguintes critérios:
- multiplataforma - conseguir escrever bibliotecas multiplataforma usando programação reativa e distribuí-las na empresa;
 
- Segurança nula - o sistema do tipo Kotlin nos protege de " erros de bilhões de dólares ", portanto valores nulos devem ser válidos (por exemplo, Observable<String?>);
 
- covariância e contravariância é outro recurso muito útil do Kotlin, que possibilita, por exemplo, converter com segurança o tipo Observable<String>paraObservable<CharSequence>.
 
Nós no Badoo decidimos não esperar o tempo à beira-mar e criamos uma biblioteca desse tipo. Como você deve ter adivinhado, chamamos Reaktive e postamos no 
GitHub .
Neste artigo, examinaremos mais de perto as expectativas de programação reativa de Kotlin e veremos como as capacidades do Reaktive as correspondem.
Três benefícios naturais de Reaktive
Multiplataforma
A primeira vantagem 
natural é mais importante. Atualmente, nossas equipes de iOS, Android e Mobile Web existem separadamente. Os requisitos são gerais, o design é o mesmo, mas cada equipe faz seu próprio trabalho.
O Kotlin permite escrever código multiplataforma, mas você precisa esquecer a programação reativa. E eu gostaria de poder escrever bibliotecas compartilhadas usando programação reativa e distribuí-las na empresa ou fazer upload no GitHub. Potencialmente, essa abordagem pode reduzir significativamente o tempo de desenvolvimento e a quantidade total de código.
Segurança nula
É mais sobre a falha do Java e do RxJava2. Em resumo, nulo não pode ser usado. Vamos tentar descobrir o porquê. Dê uma olhada nesta interface Java:
 public interface UserDataSource {   Single<User> load(); } 
O resultado pode ser nulo? Para evitar ambiguidade, nulo não é permitido no RxJava2. E se você ainda precisar, é Talvez e Opcional. Mas em Kotlin não existem tais problemas. Podemos dizer que 
Single<User> e 
Single<User?> tipos diferentes e todos os problemas aparecem no estágio de compilação.
Covariância e contravariância
Esta é uma característica distintiva do Kotlin, algo que falta muito em Java. Você pode ler mais sobre isso no 
manual . Vou dar apenas alguns exemplos interessantes de quais problemas surgem ao usar o RxJava no Kotlin.
Covariância :
 fun bar(source: Observable<CharSequence>) { } fun foo(source: Observable<String>) {   bar(source)  
Como 
Observable é uma interface Java, esse código não é compilado. Isso ocorre porque tipos genéricos em Java são invariantes. É claro que você pode usar, mas usar operadores como o scan novamente levará a um erro de compilação:
 fun bar(source: Observable<out CharSequence>) {   source.scan { a, b -> "$a,$b" }  
A instrução de varredura é diferente, pois seu tipo genérico “T” é de entrada e saída. Se Observable fosse a interface Kotlin, seu tipo T poderia ser indicado como fora e isso resolveria o problema:
 interface Observable<out T> {   … } 
E aqui está um exemplo com contravariância:
 fun bar(consumer: Consumer<String>) { } fun foo(consumer: Consumer<CharSequence>) {   bar(consumer)  
Pelo mesmo motivo que no exemplo anterior (tipos genéricos em Java são invariáveis), este exemplo não é compilado. A adição resolverá o problema, mas novamente não cem por cento:
 fun bar(consumer: Consumer<in String>) {   if (consumer is Subject) {       val value: String = consumer.value  
Bem, por tradição, no Kotlin esse problema é resolvido usando in na interface:
 interface Consumer<in T> {   fun accept(value: T) } 
Assim, variabilidade e contravariância de tipos genéricos são a terceira vantagem 
natural da biblioteca Reaktive.
Kotlin + Reativo = Reaktive
Passamos à coisa principal - a descrição da biblioteca Reaktive.
Aqui estão alguns dos seus recursos:
- É multiplataforma, o que significa que você pode finalmente escrever código geral. No Badoo, consideramos esse um dos benefícios mais importantes.
 
- Está escrito em Kotlin, o que nos dá as vantagens descritas acima: não há restrições para nulo, variação / contravariância. Isso aumenta a flexibilidade e fornece segurança durante a compilação.
 
- Não há dependência de outras bibliotecas, como RxJava, RxSwift etc., o que significa que não há necessidade de levar a funcionalidade da biblioteca a um denominador comum.
 
- API pura. Por exemplo, a interface ObservableSourceno Reaktive é chamada simplesmenteObservable, e todos os operadores são funções de extensão localizadas em arquivos separados. Não há classes de Deus de 15.000 linhas. Isso torna possível aumentar facilmente a funcionalidade sem fazer alterações nas interfaces e classes existentes.
 
- Suporte para agendadores (usando observeOnfamiliaressubscribeOneobserveOn).
 
- Compatível com RxJava2 (interoperabilidade), fornecendo conversão de origem entre Reaktive e RxJava2 e a capacidade de reutilizar agendadores do RxJava2.
 
- Conformidade com o ReactiveX .
 
Gostaria de falar um pouco mais sobre os benefícios que recebemos devido ao fato de a biblioteca estar escrita em Kotlin.
- No Reaktive, valores nulos são permitidos, porque no Kotlin é seguro. Aqui estão alguns exemplos interessantes:
 - observableOf<String>(null) //
- val o1: Observable<String?> = observableOf(null)
 - val o2: Observable<String> = o1 // ,
- val o1: Observable<String?> = observableOf(null)
 - val o2: Observable<String> = o1.notNull() // , null
- val o1: Observable<String> = observableOf("Hello")
 - val o2: Observable<String?> = o1 //
- val o1: Observable<String?> = observableOf(null)
 - val o2: Observable<String> = observableOf("Hello")
 - val o3: Observable<String?> = merge(o1, o2) //
 - val o4: Observable<String> = merge(o1, o2) // ,
 
 Variação também é uma grande vantagem. Por exemplo, na interfaceObservable, o tipo T é declarado comoout, o que torna possível escrever algo como o seguinte:
 
  fun foo() {   val source: Observable<String> = observableOf("Hello")   bar(source) 
 
É assim que a biblioteca se parece hoje:- status no momento da redação: alfa (são possíveis algumas alterações na API pública);
 
- plataformas suportadas: JVM e Android;
 
- fontes suportadas: Observable,Maybe,SingleeCompletable;
 
- um número bastante grande de operadores é suportado, incluindo map, filter, flatMap, concatMap, combineLatest, zip, merge e outros (a lista completa pode ser encontrada no GitHub );
 
- Os seguintes agendadores são suportados: computação, IO, trampolim e principal;
 
- assuntos: PublishSubject e BehaviorSubject;
 
- a contrapressão ainda não é suportada, mas estamos pensando na necessidade e na implementação desse recurso.
 
Quais são nossos planos para o futuro próximo:- começar a usar o Reaktive em nossos produtos (atualmente estamos considerando opções);
 
- Suporte a JavaScript (solicitação pull já em revisão);
 
- suporte para iOS
 
- publicar artefatos no JCenter (atualmente usando o serviço JitPack);
 
- Documentação
 
- aumento do número de operadores suportados;
 
- Testes
 
- mais plataformas - solicitações pull são bem-vindas!
 
Você pode experimentar a biblioteca agora e pode encontrar tudo o que precisa no 
GitHub . Compartilhe sua experiência e faça perguntas. Ficaremos gratos por qualquer feedback.