Como as Características em Scala evitam o "erro de diamante"?


16

(Nota: usei 'erro' em vez de 'problema' no título por razões óbvias ..;)).

Fiz algumas leituras básicas sobre Traits in Scala. Eles são semelhantes às interfaces em Java ou C #, mas permitem a implementação padrão de um método.

Fiquei pensando: isso não pode causar um caso do "problema do diamante", e é por isso que muitos idiomas evitam a herança múltipla em primeiro lugar?

Se sim, como Scala lida com isso?


Compartilhar sua pesquisa ajuda a todos . Conte-nos o que você tentou e por que ele não atendeu às suas necessidades. Isso demonstra que você reservou um tempo para tentar ajudar a si mesmo, evita reiterar respostas óbvias e, acima de tudo, ajuda a obter uma resposta mais específica e relevante. Veja também How to Ask
gnat

2
@gnat: esta é uma questão conceitual, não uma questão concreta. Se ele estava perguntando "Eu tenho essa aula em Scala e está me dando problemas que eu acho que podem estar relacionados ao Problema do Diamante, como faço para corrigi-lo?" seu comentário seria apropriado, mas a pergunta pertenceria a SO. : P
Mason Wheeler

@MasonWheeler Também fiz algumas leituras básicas sobre Scala. E a primeira pesquisa por "diamante" no que li me deu a resposta: "Uma característica possui todos os recursos da construção da interface Java. Mas as características podem ter implementado métodos nelas. Se você conhece o Ruby, as características são semelhantes. às mixins de Ruby. Você pode misturar muitas características em uma única classe. As características não podem assumir parâmetros de construtor, mas que não se comportam como classes. Isso permite que você tenha algo que se aproxime de herança múltipla sem o problema do diamante ". Falta de esforço nesta questão parece bastante flagrante #
26614

7
A leitura dessa afirmação não indica COMO, exatamente, isso acontece.
Michael Brown

Respostas:


21

O problema do diamante é a incapacidade de decidir qual implementação do método escolher. O Scala resolve isso definindo qual implementação escolher como parte das especificações de idioma ( leia a parte sobre Scala neste artigo da Wikipedia) ).

É claro que a mesma definição de ordem também pode ser usada na herança múltipla de classe, então por que se preocupar com características?

A razão pela qual o IMO são construtores. Os construtores têm várias limitações que os métodos regulares não têm - eles podem ser chamados apenas uma vez por objeto, precisam ser chamados para cada novo objeto, e o construtor de uma classe filho deve chamar seu construtor pai como primeira instrução (a maioria das linguagens faça isso implicitamente para você, se você não precisar passar parâmetros).

Se B e C herdam A e D herdam B e C, e ambos os construtores de B e C chamam o construtor de A, o construtor de D chama o construtor de A duas vezes. Definir quais implementações escolher como Scala fez com os métodos não funcionará aqui, porque ambos construtores de B e C devem ser chamados.

As características evitam esse problema, pois não possuem construtores.


1
É possível usar a linearização C3 para chamar os construtores uma vez e apenas uma vez - é assim que o Python faz herança múltipla. Do alto da minha cabeça, a linearização para o diamante D <B | C <Um diamante é D -> B -> C -> A. Além disso, uma pesquisa no Google me mostrou que os traços de Scala podem ter variáveis ​​mutáveis, então certamente há um construtor em algum lugar lá? Mas se ele está usando composição sob a capa (eu não sei, nunca usado Scala) não é difícil ver que B e C poderiam compartilhar em instância de A ...
Doval

... As características parecem uma maneira muito concisa de expressar todo o padrão que combina a herança de interface e composição + delegação, que é a maneira correta de reutilizar o comportamento.
Doval

@Doval Minha experiência de construtores em Python multiplicavelmente herdado é que eles são uma dor real. Cada construtor não pode saber em qual ordem será chamado, portanto, não sabe qual é a assinatura do construtor pai. A solução usual é que todo construtor adote vários argumentos de palavra-chave e passe os não utilizados para seu super construtor, mas se você precisar trabalhar com uma classe existente que não segue essa convenção, não poderá herdar com segurança de isto.
James_pic

Outra questão é por que o C ++ não escolheu a política sã para o problema do diamante?
usuário

20

Scala evita o problema do diamante por algo chamado "linearização de características". Basicamente, ele procura a implementação do método nas características que você estende da direita para a esquerda. Exemplo simples:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

Dito isto, a lista de características pesquisadas pode conter mais do que as que você deu explicitamente, pois podem estender outras características. Uma explicação detalhada é dada aqui: Traços como modificações empilháveis e um exemplo mais completo da linearização aqui: Por que não herança múltipla?

Eu acredito que em outras linguagens de programação esse comportamento às vezes é chamado de "ordem de resolução de método" ou "MRO".

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.