Em muitos anos de programação OO, entendi o que são sindicatos discriminados, mas nunca senti muita falta deles. Eu tenho feito recentemente alguma programação funcional em C # e agora acho que desejo continuar com eles. Isso me deixa desconcertante, porque, diante disso, o conceito de uniões discriminadas parece bastante independente da dicotomia funcional / OO.
Existe algo inerente à programação funcional que torna os sindicatos discriminados mais úteis do que seria no OO, ou é que, ao me forçar a analisar o problema de uma maneira "melhor", simplesmente aprimorei meus padrões e agora exijo uma melhor modelo?
Respostas:
As uniões discriminadas realmente brilham em conjunto com a correspondência de padrões, onde você seleciona comportamentos diferentes, dependendo dos casos. Mas esse padrão é fundamentalmente antitético aos princípios puros de OO.
Em OO puro, as diferenças de comportamento devem ser definidas pelos próprios tipos (objetos) e encapsuladas. Portanto, a equivalência à correspondência de padrões seria chamar um único método no próprio objeto, que será sobrecarregado pelos subtipos em questão para definir um comportamento diferente. Inspecionar o tipo de um objeto de fora (que é o que a correspondência de padrões faz) é considerado um antipadrão.
A diferença fundamental é que dados e comportamento são separados na programação funcional, enquanto dados e comportamento são encapsulados juntos no OO.
Esta é a razão histórica. Uma linguagem como C # está se desenvolvendo de uma linguagem OO clássica para uma linguagem de múltiplos paradigmas, incorporando cada vez mais recursos de funções.
fonte
List<A>
o métodoB Match<B>(B nil, Func<A,List<A>,B> cons)
. Por exemplo, esse é exatamente o padrão que o Smalltalk usa para booleanos. Isso também é basicamente como Scala lida com isso. O fato de várias classes serem usadas é um detalhe de implementação que não precisa ser exposto.isEmpty
método que verifica se a referência ao próximo nó é nula.Tendo programado em Pascal e Ada antes de aprender programação funcional, não associo uniões discriminadas a programação funcional.
Os sindicatos discriminados são, de alguma forma, o dual da herança. O primeiro permite adicionar operações facilmente em um conjunto fixo de tipos (aqueles na união), e a herança permite adicionar tipos facilmente com um conjunto fixo de operações. (Como adicionar facilmente ambos é chamado de problema de expressão ; esse é um problema especialmente difícil para idiomas com um sistema de tipo estático.)
Devido à ênfase do OO nos tipos e à dupla ênfase da programação funcional nas funções, as linguagens de programação funcional têm uma afinidade natural pelos tipos de união e oferecem estruturas sintáticas para facilitar seu uso.
fonte
As técnicas de programação imperativa, como frequentemente usadas no OO, dependem frequentemente de dois padrões:
null
para indicar "sem valor" ou falha.O paradigma funcional normalmente evita ambos, preferindo retornar um tipo composto que indica motivo de sucesso / falha ou valor / nenhum valor.
As uniões discriminadas se ajustam à conta desses tipos de compostos. Por exemplo, na primeira instância, você pode retornar
true
ou alguma estrutura de dados que descreve a falha. No segundo caso, uma união que contém um valor, ounone
,nil
etc. O segundo caso é tão comum, que muitas linguagens funcionais têm um "talvez" ou do tipo "opção" built-in para representar esse valor / nenhum sindicato.Ao mudar para um estilo funcional com, por exemplo, C #, você encontrará rapidamente a necessidade desses tipos de compostos.
void/throw
enull
simplesmente não se sente bem com esse código. E os sindicatos discriminados (DUs) se encaixam bem. Assim, você se viu querendo eles, assim como muitos de nós.A boa notícia é que existem muitas bibliotecas por aí que modelam DUs em, por exemplo, C # (dê uma olhada na minha própria biblioteca Succinc <T> , por exemplo).
fonte
Geralmente, os tipos de soma seriam menos úteis nas linguagens OO convencionais, pois estão resolvendo um tipo de problema semelhante ao da subtipo OO. Uma maneira de olhar para eles é que ambos lidam com subtipagem, mas OO é,
open
ou seja, é possível adicionar subtipos arbitrários a um tipo pai e tipos de soma são,closed
ou seja, é possível determinar antecipadamente quais subtipos são válidos.Agora, muitas linguagens OO combinam subtipagem com outros conceitos, como estruturas herdadas, polimorfismo, digitação de referência etc. para torná-las geralmente mais úteis. A consequência é que eles tendem a ser mais trabalho para configurar (com classes e construtores e outros enfeites) para que tendiam a não ser usados para coisas como
Result
s eOption
s e assim sucessivamente até digitação genérico tornou-se comum.Eu diria também que o foco nas relações do mundo real que a maioria das pessoas aprendeu quando começou a programação OO, como Dog isa Animal, significava que Inteiro é um resultado ou Erro é um resultado um pouco estranho. Embora as idéias sejam bastante semelhantes.
Quanto ao motivo pelo qual as linguagens funcionais podem preferir a digitação fechada à aberta, um possível motivo é que elas tendem a preferir a correspondência de padrões. Isso é útil para o polimorfismo de função, mas também funciona muito bem com tipos fechados, pois o compilador pode verificar estaticamente se a correspondência está cobrindo todos os subtipos. Isso pode fazer com que o idioma pareça mais consistente, embora eu não acredite que exista algum benefício inerente (eu poderia estar enganado).
fonte
sealed
significa "só pode ser estendido na mesma unidade de compilação", o que permite fechar o conjunto de subclasses em tempo de design.Felizmente, Swift usa sindicatos discretos, exceto que os chama de "enums". As enums são uma das cinco categorias fundamentais de objetos no Swift, que são classe, estrutura, enumeração, tupla e fechamento. Os opcionais Swift são enumerações que são uniões discriminatórias e são absolutamente essenciais para qualquer código Swift.
Portanto, a premissa de que "os sindicatos discriminadores estão associados à programação funcional" está errada.
fonte