tl; dr: Haveria uma definição independente de idioma dos Símbolos e uma razão para tê-los em outros idiomas?
Então, por que o criador do Ruby usou o conceito de symbols
na linguagem?
Eu pergunto isso da perspectiva de um programador não-rubi. Eu aprendi muitas outras línguas e não encontrei nenhuma delas, a necessidade de especificar se eu estava lidando ou não com o que Ruby chama symbols
.
A principal questão é: o conceito de symbols
Ruby existe para desempenho ou apenas algo que é necessário devido à maneira como a linguagem é escrita?
Um programa em Ruby seria mais leve e / ou mais rápido que seu, digamos, equivalente em Python ou Javascript? Se sim, seria por causa disso symbols
?
Como uma das intenções de Ruby é ser fácil de ler e escrever para humanos, seus criadores não poderiam facilitar o processo de codificação implementando essas melhorias no próprio intérprete (como pode ser em outros idiomas)?
Parece que todo mundo quer saber apenas o que symbols
são e como usá-los, e não por que eles estão lá em primeiro lugar.
fonte
Respostas:
O criador de Ruby, Yukihiro "Matz" Matsumoto, postou uma explicação sobre como Ruby foi influenciado por Lisp, Smalltalk, Perl (e a Wikipedia também diz Ada e Eiffel):
Em qualquer compilador, você gerenciará identificadores para funções, variáveis, blocos nomeados, tipos e assim por diante. Normalmente, você as armazena no compilador e as esquece no executável produzido, exceto quando você adiciona informações de depuração.
No Lisp, esses símbolos são recursos de primeira classe, hospedados em pacotes diferentes, o que significa que você pode adicionar símbolos novos em tempo de execução, vinculá-los a diferentes tipos de objetos. Isso é útil na metaprogramação, pois você pode ter certeza de que não terá colisões de nomes com outras partes do código.
Além disso, os símbolos são internados no tempo de leitura e podem ser comparados por identidade, que é uma maneira eficiente de ter novos tipos de valores (como números, mas abstratos). Isso ajuda a escrever código onde você usa valores simbólicos diretamente, em vez de definir seus próprios tipos de enumeração apoiados por números inteiros. Além disso, cada símbolo pode conter dados adicionais. É assim que, por exemplo, o Emacs / Slime pode anexar metadados do Emacs diretamente à lista de propriedades de um símbolo.
A noção de símbolo é central em Lisp. Dê uma olhada, por exemplo, no PAIP (Paradigmas de programação de inteligência artificial: estudos de caso em Common Lisp, Norvig) para obter exemplos detalhados.
fonte
Bem, eles não precisavam "estritamente", eles escolheram. Além disso, observe que, estritamente falando,
Symbol
s não fazem parte do idioma, eles fazem parte da biblioteca principal. Eles possuem sintaxe literal no nível do idioma, mas funcionariam da mesma forma se você tivesse que construí-los chamandoSymbol::new
.Você não disse o que são essas "muitas outras línguas", mas aqui está apenas um pequeno trecho de linguagem que possui um
Symbol
tipo de dados como o Ruby:Existem também outros idiomas que fornecem os recursos de
Symbol
s de uma forma diferente. Em Java, por exemplo, os recursos de RubyString
s são divididos em dois (na verdade três) tipos:String
eStringBuilder
/StringBuffer
. Por outro lado, as características de RubySymbol
tipo são dobradas para o JavaString
Tipo: JavaString
s podem ser internado , strings literais eString
s que são o resultado de tempo de compilação expressões constantes avaliadas são automaticamente internado, geradas dinamicamenteString
s podem ser internado chamando oString.intern
método Um internadoString
em Java é exatamente como umSymbol
em Ruby, mas não é implementado como um tipo separado, é apenas um estado diferente do que um JavaString
pode estar disponível. (Nota: nas versões anteriores do Ruby,String#to_sym
costumava ser chamadoString#intern
e esse método ainda existe hoje como um alias herdado.)Symbol
s são antes de tudo um tipo de dados com semântica específica . Essa semântica também possibilita implementar algumas operações de alto desempenho (por exemplo, testes rápidos de igualdade O (1)), mas esse não é o objetivo principal.Symbol
s não são necessários na linguagem Ruby, o Ruby funcionaria perfeitamente sem eles. Eles são puramente um recurso de biblioteca. Há exatamente um lugar no idioma vinculado aSymbol
s: umadef
expressão de definição de método é avaliada comoSymbol
denotando o nome do método que está sendo definido. No entanto, essa é uma alteração bastante recente, antes disso, o valor de retorno simplesmente não foi especificado. A RM simplesmente avaliounil
, Rubinius avaliou umRubinius::CompiledMethod
objeto e assim por diante. Também seria possível avaliar para umUnboundMethod
... ou apenas umString
.Não tenho certeza do que você está perguntando aqui. O desempenho é principalmente uma questão de qualidade de implementação, não de linguagem. Além disso, o Node nem sequer é uma linguagem, é uma estrutura de E / S registrada para ECMAScript. Executando um script equivalente no IronPython e MRI, o IronPython provavelmente será mais rápido. Executando um script equivalente no CPython e JRuby + Truffle, é provável que o JRuby + Truffle seja mais rápido. Isso não tem nada a ver com
Symbol
s, mas com a qualidade da implementação: o JRuby + Truffle possui um compilador de otimização agressiva, além de todo o mecanismo de otimização de uma JVM de alto desempenho, o CPython é um intérprete simples.No.
Symbol
s não são uma otimização de compilador. Eles são um tipo de dados separado com semântica específica. Eles não são como os flonums do YARV , que são uma otimização interna privada paraFloat
s. A situação não é a mesma que paraInteger
,Bignum
eFixnum
, o que deve ser um detalhe otimização interna privada invisível, mas infelizmente não é. (Isto é, finalmente, vai ser fixado em Ruby 2.4, que removeFixnum
eBignum
e folhas apenasInteger
.)Fazer isso da maneira que o Java faz, como um estado especial de
String
s normal, significa que você sempre precisa ser cauteloso sobre se seusString
s estão ou não nesse estado especial e sob quais circunstâncias eles estão automaticamente nesse estado especial e quando não. Esse é um fardo muito maior do que simplesmente ter um tipo de dados separado.Symbol
é um tipo de dados que denota o conceito de nome ou rótulo .Symbol
s são objetos de valor , imutáveis, geralmente imediatos (se a linguagem distingue uma coisa dessas), apátridas e sem identidade. DoisSymbol
s iguais são também garantidos idênticos, ou seja, doisSymbol
s iguais são na verdade o mesmoSymbol
. Isso significa que igualdade de valor e igualdade de referência são a mesma coisa e, portanto, igualdade é eficiente e O (1).Os motivos para tê-los em um idioma são realmente os mesmos, independentemente do idioma. Alguns idiomas dependem mais deles do que outros.
Na família Lisp, por exemplo, não há conceito de "variável". Em vez disso, você tem
Symbol
s associados a valores.Em linguagens com capacidades reflexivas ou introspectivos,
Symbol
s muitas vezes são usados para indicar os nomes das entidades reflectidas nas APIs de reflexão, por exemplo, em Ruby,Object#methods
,Object#singleton_methods
,Object#public_methods
,Object#protected_methods
, eObject#public_methods
retornar umArray
deSymbol
s (embora eles poderiam muito bem retornar umArray
deMethod
s).Object#public_send
usa umSymbol
denotando o nome da mensagem a ser enviada como argumento (embora também aceite umString
,Symbol
seja mais semanticamente correto).No ECMAScript,
Symbol
s são um alicerce fundamental para tornar o ECMAScript seguro para os recursos no futuro. Eles também desempenham um grande papel na reflexão.fonte
Os símbolos são úteis no Ruby e você os verá em todo o código Ruby, porque cada símbolo é reutilizado toda vez que é referenciado. Isso é uma melhoria de desempenho em relação às cadeias, porque cada uso de uma cadeia que não é salva em uma variável cria um novo objeto na memória. Por exemplo, se eu usar a mesma sequência várias vezes como uma chave de hash:
A cadeia "a" é criada 101.000 vezes na memória. Se eu usasse um símbolo:
O símbolo
:a
ainda é um objeto na memória. Isso torna os símbolos muito mais eficientes que as strings.ATUALIZAÇÃO Aqui está uma referência (retirada da Codecademy ) que demonstra a diferença de desempenho:
Aqui estão meus resultados para o meu MBP:
Há uma clara diferença no uso de strings x símbolos para apenas identificar chaves em um hash.
fonte
"a"
fato seja uma string nova, acho que no seu exemplo haverá exatamente duas"a"
(e uma implementação pode até compartilhar a memória até que uma delas seja mutada). Para criar milhões de strings, você provavelmente precisará usar String.new ("a"). Mas eu não sou muito versado em Ruby, então talvez eu esteja errado.string_AZ[String.new("r")]
para ver se isso faz diferença. Eu recebo 21ms para strings (versão original), 7ms com símbolos e 50ms com strings novos a cada vez. Então, eu diria que as strings não são alocadas tanto com a"r"
versão literal .