segunda-feira, 23 de novembro de 2015

Swift: Brincando com Enum

E aí galera, tudo de boa?
Tô há um tempão sem escrever! Mas estou com algumas novidades, atualmente estou trabalhando com mobile, no Elo7 e claro não desisti do funcional, ando estudando haskell de uma forma um pouco mais compromissada, todas as quintas-feiras estou participando de um dojo de haskell, quem quiser, só mandar um email no abner.terribili@gmail.com ou se caso estiver muito longe e queira participar de longe, aceitamos pull requests em wando-hs :)

Para estrear o mobile aqui no Hackeando, que tal conhecermos o poder do Enum no Swift?

O que é Enum?

Enum, no Swift, nada mais é do que a definição de um tipo para um grupo de valores que têm alguma relação.

Abstrato né?

Imagine que você precisa filtrar uma lista, é um exemplo meio recorrente no meu dia a dia, por isso o escolhi. Então, temos uma lista de jogos, e desejo filtrar por plataforma, o enum em Java, seria algo do tipo:

enum GameFilter {
  PS3("playstation3"),
  PS4("playstation4"),
  XBOX360("xbox360"),
  XBOX_ONE("xboxOne");

  final String tag;
  GameFilter(String tag) {
    this.tag = tag;
  }

  @Override
  public String toString() {
    return tag;
  }
}

O tag aí serve para podermos pegar o valor que será aplicado no momento de filtro, ou seja, para montar a url:

www.lojadejogos.com/jogos?plataforma=playstation3

Podemos ver que, usando Java, precisamos de uma pequena field auxiliar para guardarmos o valor da tag. Vamos dar uma olhadinha no GameFilter construído em Swift:

enum GameFilter: String {
  case PS3 = "playstation3"
  case PS4 = "playstation4"
  case XBOX360 = "xbox360"
  case XBOX_ONE = "xboxOne"

  func description() -> String {return self.rawValue}
}

São quase iguais né? Com a diferença que ao invés de uma field auxiliar para armazenar o valor da tag, nós baseamos nosso enum em String.

Isso é muito chato e básico!

Vamos fazer algo mais legal, imagina que você quer ter um pouco mais de segurança com o que você trabalha (se costuma programar, já deve ter tomado um NullPointerException na cara), semântica e é hipster, sendo assim decidiu implementar um Optional na mão, isso tem fins didáticos, pois no Swift já temos uso extensivo de Optional.

Então temos nosso enum:

enum Optional<T> {
    case None
    case Some(T)

    init(_ value: T) {
        self = .Some(value)
    }

    init() {
        self = .None
    }

    func getOrElse() -> Any {
        switch self {
        case .Some(let value):
            return value
        default:
            return Optional.None
        }
    }
}

Na linha enum Optional<T> {..} nós definimos o nome do Enum, e declaramos um tipo genérico que será usado para inferir o tipo.

Com isso nós implementamos dois construtores, um que recebe um valor qualquer init(_ value: T) {self = .Some(value)} e outro que não recebe nenhum argumento: init() {self = .None}, fica meio maluco esse bind self=**, mas entenda, estamos atribuindo o valor da instância de acordo com o construtor que é usado. :)

E por fim, definimos um método que devolve qualquer coisa (Any), o objeto que está envelopado ou um Optional.None, e que faz a validação pra gente a respeito da instância que está chamando o getOrElse().

Para usarmos nosso Optional é simples:

let none = Optional()
none.getOrElse() //-> ()

let some = Optional(10.0)
some.getOrElse() //-> 10

Podemos ver que o construtor vazio devolve None, ou seja, nada, já o construtor que tem parametro, devolve o valor que foi passado, e olha que legal o Generics funcionando no Swift!

E aí, deu pra sentir a força do enum no Swift? E da pra fazer MUITO mais. Isso é só o básico :)

Só pra finalizar, se você já deu uma olhadinha de leve em Swift, com certeza o conceito de Optional não passou batido, pra nossa implementação ficar bacanuda, o que acham de implementarmos o sufixo para fazer unwrap do valor?

postfix operator *? {}
postfix func *? <T>(value: Optional<T>) -> Any {
    return value.getOrElse()
}

O código não é tão claro, mas basicamente estamos definindo um operador, do tipo sufixo, ou seja, que é utilizado no fim da expressão. Algo como:

Optional("xablau")*? //-> "xablau"

E por hoje é só galera, se rolar alguma dúvida ou sugestão, só comentar abaixo ou me enviar um email
Abraços