Usos de Nulo / Nada / Unidade em Scala

95

Acabei de ler: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

Pelo que entendi, Nullé uma característica e sua única instância é null.

Quando um método recebe um argumento Nulo, só podemos passar uma Nullreferência ou nulldiretamente, mas nenhuma outra referência, mesmo que seja nula ( nullString: String = nullpor exemplo).

Eu só me pergunto em quais casos usar essa Nullcaracterística pode ser útil. Há também o traço Nothing para o qual eu realmente não vejo mais exemplos.


Também não entendo bem qual é a diferença entre usar Nothing e Unit como tipo de retorno, já que ambos não retornam nenhum resultado, como saber qual usar quando tenho um método que executa o log por exemplo?


Você usa Unit / Null / Nothing como algo mais do que um tipo de retorno?

Sebastien Lorber
fonte

Respostas:

80

Você só usa Nothing se o método nunca retornar (o que significa que ele não pode ser concluído normalmente com o retorno, ele pode lançar uma exceção). Nada nunca é instanciado e existe para o benefício do sistema de tipos (para citar James Iry: "O motivo pelo qual Scala tem um tipo inferior está ligado à sua capacidade de expressar variação nos parâmetros de tipo." ). Do artigo que você vinculou a:

Um outro uso de Nothing é como um tipo de retorno para métodos que nunca retornam. Isso faz sentido se você pensar sobre isso. Se o tipo de retorno de um método é Nothing, e não existe absolutamente nenhuma instância de Nothing, então tal método nunca deve retornar.

Seu método de registro retornaria Unit. Há um valor Unit para que ele possa realmente ser retornado. Dos documentos da API :

Unidade é um subtipo de scala.AnyVal. Existe apenas um valor do tipo Unit, () e não é representado por nenhum objeto no sistema de tempo de execução subjacente. Um método com tipo de retorno Unit é análogo a um método Java que é declarado nulo.

Nathan Hughes
fonte
2
Obrigado, por "nunca retorna", você quer dizer que a chamada está bloqueando indefinidamente (por exemplo, o método de inicialização do agendador de trabalho?)
Sebastien Lorber
3
@Sabastien: não retorna normalmente, pode gerar uma exceção (ver james-iry.blogspot.com/2009/08/… ). se a chamada de bloqueio terminar apenas com o lançamento de uma exceção, isso contaria. obrigado pela pergunta, isso precisava de esclarecimento.
Nathan Hughes
18

O artigo que você cita pode ser enganoso. O Nulltipo existe para compatibilidade com a máquina virtual Java Java e Java em particular.

Devemos considerar que Scala :

  • é totalmente orientado a objetos: todo valor é um objeto
  • é fortemente tipado: cada valor deve ter um tipo
  • precisa lidar com nullreferências para acessar, por exemplo, bibliotecas Java e código

assim, torna-se necessário definir um tipo para o nullvalor, que é o Nulltraço, e tem nullcomo única instância.

Não há nada especialmente útil no Nulltipo, a menos que você seja o sistema de tipos ou esteja desenvolvendo no compilador. Em particular, não vejo nenhuma razão sensata para definir um Nullparâmetro de tipo para um método, uma vez que você não pode passar nada alémnull

pagoda_5b
fonte
isso é verdade, então no final não há outro uso de Null?
Sebastien Lorber
@SebastienLorber editou a resposta. Na verdade, não consigo ver nenhum uso para o desenvolvedor médio. Talvez outra pessoa possa pensar em algo útil.
pagoda_5b
Obrigado por isso. Se sabemos o porquê desse tipo de coisas, nós as entendemos, caso contrário, nos lembramos delas.
Sreekar
Nulo é útil quando você tem um parâmetro de tipo e pode querer retornar nulo como nesta pergunta e resposta , já que você é desencorajado a usar nulo em scala, raramente aparece, mas pode haver outros usos no sistema de tipo
Daniel Carlsson
15

Você usa Unit / Null / Nothing como algo mais do que um tipo de retorno?


Unit pode ser usado assim:

def execute(code: => Unit):Unit = {
  // do something before
  code
  // do something after
}

Isso permite que você passe um bloco arbitrário de código a ser executado.


Nullpode ser usado como um tipo inferior para qualquer valor anulável. Um exemplo é este:

implicit def zeroNull[B >: Null] =
    new Zero[B] { def apply = null }

Nothing é usado na definição de None

object None extends Option[Nothing]

Isso permite que você atribua um Nonea qualquer tipo de Optionporque Nothing'estende' tudo.

val x:Option[String] = None
EECOLOR
fonte
Está bem. Para o seu uso de Unit, você poderia ter usado um tipo genérico para que execute possa retornar esse tipo genérico se o seu bloco de código retornar algo diferente de unidade.
Sebastien Lorber
Como @drexin disse em um comentário em outra resposta, é usado principalmente para denotar um efeito colateral.
EECOLOR
6

se você usar Nothing, não há nada a fazer (incluir console de impressão) se você fizer algo, use o tipo de saídaUnit

object Run extends App {
  //def sayHello(): Nothing = println("hello?")
  def sayHello(): Unit = println("hello?")
  sayHello()
}

... então como usar Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]
Curycu
fonte
1
Além disso, Eo Optiontem que estar na posição covariante: trait Option[+E]para permitir coisas comoval x: Option[Int] = None
vim
5

Na verdade, nunca usei o Nulltipo, mas você usa Unit, onde usaria em java void. Nothingé um tipo especial porque, como Nathan já mencionou, não pode haver nenhuma instância de Nothing. Nothingé o chamado tipo de fundo, o que significa que é um subtipo de qualquer outro tipo. Este (e o parâmetro de tipo contravariante) é o motivo pelo qual você pode acrescentar qualquer valor a Nil- que é um List[Nothing]- e a lista será desse tipo de elemento. Nonetambém se do tipo Option[Nothing]. Cada tentativa de acessar os valores dentro de tal contêiner lançará uma exceção, porque é a única maneira válida de retornar de um método do tipo Nothing.

drexin
fonte
obrigado, eu não sabia que None estende Option [Nothing]. Faz sentido em alguns usos de genéricos, onde um subtipo pode usar Nothing (acho que é difícil encontrar um exemplo para Nulo e Unidade ...)
Sebastien Lorber
A unidade é usada, onde ocorrem efeitos colaterais, por exemplo, a mônada IO pode ser do tipo IO[Unit]para impressão no console e semelhantes.
drexin
Sim, nunca usei a mônada IO, faz sentido usá-la com Unidade (e talvez Nothing se for alguma operação IO que produz um fluxo infinito?)
Sebastien Lorber
Não, nada não faz sentido aqui.
drexin
3

Muitas vezes, nada é usado implicitamente. No código a seguir, val b: Boolean = if (1 > 2) false else throw new RuntimeException("error") a cláusula else é do tipo Nothing , que é uma subclasse de Boolean (assim como qualquer outro AnyVal). Assim, toda a atribuição é válida para o compilador, embora a cláusula else realmente não retorne nada.

Fang Zhang
fonte
1

Aqui está um exemplo de Nothingpartir scala.predef:

  def ??? : Nothing = throw new NotImplementedError

No caso de você não estar familiarizado (e os motores de busca não podem pesquisar nele), ???é a função de espaço reservado do Scala para qualquer coisa que ainda não tenha sido implementada. Assim como o de Kotlin TODO.

Você pode usar o mesmo truque ao criar objetos fictícios: substitua métodos não usados ​​por um notUsedmétodo personalizado . A vantagem de não usar ???é que você não receberá avisos de compilação para coisas que nunca pretende implementar.

David Leppik
fonte
0

Em termos de teoria das categorias, Nada é um objeto inicial e Unidade é um objeto terminal .

https://en.wikipedia.org/wiki/Initial_and_terminal_objects

Os objetos iniciais também são chamados de coterminais ou universais e os objetos terminais também são chamados de finais .

Se um objeto é inicial e terminal , ele é chamado de objeto zero ou objeto nulo .

Joe
fonte