Uniões marcadas em PHP (semelhante a Rust)

No artigo anterior , escrevi sobre como adicionar enums no PHP8.1. A votação foi bem sucedida, pelo que a questão pode ser considerada encerrada.







No entanto, essa implementação de enums é apenas parte do plano global . Hoje vamos considerar o próximo item, sindicatos marcados, em russo que se traduz como "soma-tipo".







Ainda não foi votado , mas é proposto incluí-lo no PHP 8.1 também.







Todos esses termos "tipos de dados algébricos", "tipo de soma" parecem assustadores, mas na realidade tudo é bastante simples.







Por que tudo isso é necessário?







Resultado como em ferrugem



Se você escreveu em Rust, provavelmente encontrou o enum interno Result. Em Rust, Go, etc. não há mecanismo de exceção, pois essas linguagens consideram o tratamento explícito de erros muito mais confiável. A linguagem força você a trabalhar explicitamente todas as variantes de eventos, e não lançar uma exceção na esperança de que alguém no topo saiba sobre isso e saiba como lidar com isso corretamente. (Não vamos culpar aqui, no tópico de exceções vs tipos de retorno, todo mundo tem sua própria opinião). Falando especificamente sobre Rust, o resultado de uma chamada de função que pode gerar um erro geralmente é transformado em um valor Result.







O resultado consiste em duas variantes (casos na terminologia enum do PHP): Ok e Err. Poderíamos fazer variações usando a funcionalidade de enum anterior ou mesmo constantes, mas também precisamos retornar os próprios valores. Além disso, em caso de sucesso, o valor pode ser uma string e, em caso de erro, algum outro tipo. Por exemplo, inteiro (status de resposta HTTP).







Como ficará em PHP se a votação for bem-sucedida:







enum Result {
    case Ok(public string $json);
    case Err(public int $httpStatus);
}

function requestApi($url): Result {
    //
}
      
      





agora podemos transferir essa resposta para outro lugar, e o conhecimento sobre o erro e seu tipo nunca será perdido.







Como escrevi no artigo anterior , enum é essencialmente uma classe, pode ter métodos etc. No caso de uma soma de tipo, os métodos podem ser gerais para o enum inteiro ou para um caso específico.







Aqui está um exemplo de implementação de Maybe monad (exemplo de RFC):







The Maybe Monad



(Em Rust, este tipo é chamado de Opção)







enum Maybe {
  // This is a Unit Case.
  case None {
    public function bind(callable $f) 
    {
      return $this;
    }
  };

  // This is a Tagged Case.
  case Some(private mixed $value) {
    // Note that the return type can be the Enum itself, thus restricting the return
    // value to one of the enumerated types.
    public function bind(callable $f): Maybe
    {
      // $f is supposed to return a Maybe itself.
      return $f($this->value);
    }
  };

  // This method is available on both None and Some.
  public function value(): mixed {
    if ($this instanceof None) {
      throw new Exception();
    }
    return $this->val;
  }
}
      
      





, : Some None, Some , None — None. bind. value()







RFC ,







$a = Maybe::Some("blabla");
//  $a = Maybe::None
$a->bind();
      
      





, - Result Maybe , - . pattern matching, RFC, . , :







$result = requestApi($url);

if ($result is Result::Some {%$json}) {
    //  $json
}

if ($result is Result::Err {%$httpStatus}) {
    //  $httpStatus
}
      
      





, match.









, tagged unions. , - , , tokenizer (scanner), . : , enum. , , , . . :







enum Token {
   case Comma;
   case LeftBrace;
   case RightBrace;
   case StringLiteral(public string $str);
   case Identifier(public string $identifier);
   //  ..
}
      
      





?



, RFC , , . , "" . tagged unions, , pattern matching.







Se você estiver interessado em artigos semelhantes sobre desenvolvimento, em particular, o que acontecerá a seguir com o padrão de correspondência, assine o canal de telegramas Cross Join !








All Articles