Como eu penso sobre isso:
type
é usado para definir novos tipos de união:
type Thing = Something | SomethingElse
Antes dessa definição Something
e SomethingElse
não significava nada. Agora, ambos são do tipo Thing
, que acabamos de definir.
type alias
é usado para dar um nome a algum outro tipo que já existe:
type alias Location = { lat:Int, long:Int }
{ lat = 5, long = 10 }
tem tipo { lat:Int, long:Int }
, que já era um tipo válido. Mas agora também podemos dizer que tem tipo Location
porque é um apelido para o mesmo tipo.
É importante notar que o seguinte irá compilar perfeitamente e exibir "thing"
. Mesmo que especificar thing
é um String
e aliasedStringIdentity
tem um AliasedString
, não obterá um erro que há uma incompatibilidade de tipo entre String
/ AliasedString
:
import Graphics.Element exposing (show)
type alias AliasedString = String
aliasedStringIdentity: AliasedString -> AliasedString
aliasedStringIdentity s = s
thing : String
thing = "thing"
main =
show <| aliasedStringIdentity thing
{}
sintaxe de registro, está definindo um novo tipo?{ lat:Int, long:Int }
não define um novo tipo. Esse já é um tipo válido.type alias Location = { lat:Int, long:Int }
também não define um novo tipo, apenas dá outro nome (talvez mais descritivo) a um tipo já válido.type Location = Geo { lat:Int, long:Int }
definiria um novo tipo (Location
)A chave é a palavra
alias
. No decorrer da programação, quando você quer agrupar coisas que pertencem umas às outras, você coloca em um registro, como no caso de um pontoou um registro do aluno.
Agora, se você precisar passar esses registros, terá que soletrar o tipo inteiro, como:
Se você pudesse criar um alias para um ponto, a assinatura seria muito mais fácil de escrever!
Portanto, um alias é uma abreviação para outra coisa. Aqui, é uma abreviatura para um tipo de registro. Você pode pensar nisso como dar um nome a um tipo de registro que usará com frequência. É por isso que é chamado de alias - é outro nome para o tipo de registro simples que é representado por
{ x:Int, y:Int }
Considerando que
type
resolve um problema diferente. Se você está vindo de OOP, é o problema que você resolve com herança, sobrecarga de operador, etc. - às vezes, você deseja tratar os dados como algo genérico e às vezes deseja tratá-los como algo específico.Um lugar comum onde isso acontece é ao passar mensagens - como o sistema postal. Ao enviar uma carta, você deseja que o sistema postal trate todas as mensagens como a mesma coisa; portanto, você só precisa projetar o sistema postal uma vez. Além disso, o trabalho de roteamento da mensagem deve ser independente da mensagem contida nela. É apenas quando a carta chega ao seu destino que você se preocupa com a mensagem.
Da mesma forma, podemos definir a
type
como a união de todos os diferentes tipos de mensagens que podem acontecer. Digamos que estejamos implementando um sistema de mensagens entre estudantes universitários para seus pais. Portanto, há apenas duas mensagens que os universitários podem enviar: 'Preciso de dinheiro para cerveja' e 'Preciso de cuecas'.Portanto, agora, quando projetamos o sistema de roteamento, os tipos de nossas funções podem apenas passar
MessageHome
, em vez de nos preocuparmos com todos os diferentes tipos de mensagens que poderiam ser. O sistema de roteamento não se importa. Ele só precisa saber que é umMessageHome
. É apenas quando a mensagem chega ao seu destino, a casa dos pais, que você precisa descobrir o que é.Se você conhece a arquitetura Elm, a função de atualização é uma declaração de caso gigante, porque esse é o destino de onde a mensagem é roteada e, portanto, processada. E usamos tipos de união para ter um único tipo com o qual lidar ao passar a mensagem, mas então podemos usar uma instrução case para descobrir exatamente que mensagem era, para que possamos lidar com isso.
fonte
Deixe-me complementar as respostas anteriores focalizando os casos de uso e fornecendo um pouco de contexto nas funções e módulos do construtor.
Usos de
type alias
Criar um alias e uma função construtora para um registro
Este é o caso de uso mais comum: você pode definir um nome alternativo e uma função construtora para um tipo específico de formato de registro.
Definir o alias do tipo implica automaticamente a seguinte função de construtor (pseudocódigo):
Person : String -> Int -> { name : String, age : Int }
Isso pode ser útil, por exemplo, quando você deseja escrever um decodificador Json.
Especifique os campos obrigatórios.
Às vezes, eles chamam de "registros extensíveis", o que pode ser enganoso. Esta sintaxe pode ser usada para especificar que você está esperando algum registro com campos específicos presentes. Tal como:
Em seguida, você pode usar a função acima como esta (por exemplo, em sua visualização):
A palestra de Richard Feldman no ElmEurope 2017 pode fornecer mais informações sobre quando vale a pena usar esse estilo.
Renomeando coisas
Você pode fazer isso, porque os novos nomes podem fornecer um significado extra posteriormente em seu código, como neste exemplo
Talvez um exemplo melhor desse tipo de uso no núcleo seja
Time
.Reexpondo um tipo de um módulo diferente
Se você estiver escrevendo um pacote (não um aplicativo), pode ser necessário implementar um tipo em um módulo, talvez em um módulo interno (não exposto), mas deseja expor o tipo de um módulo diferente (público). Ou, alternativamente, você deseja expor seu tipo de vários módulos.
Task
no núcleo e Http.Request em HTTP são exemplos para o primeiro, enquanto o Json.Encode.Value e Json.Decode.Value par é um exemplo do mais tarde.Você só pode fazer isso quando, de outra forma, deseja manter o tipo opaco: você não expõe as funções do construtor. Para obter detalhes, consulte os usos
type
abaixo.É importante notar que nos exemplos acima apenas o # 1 fornece uma função de construtor. Se você expor seu alias de tipo em # 1 assim,
module Data exposing (Person)
irá expor o nome do tipo, bem como a função do construtor.Usos de
type
Defina um tipo de união marcada
Este é o caso de uso mais comum, um bom exemplo disso é o
Maybe
tipo em núcleo :Ao definir um tipo, você também define suas funções de construtor. No caso de Maybe estes são (pseudocódigo):
O que significa que se você declarar este valor:
Você pode criá-lo por qualquer
ou
As tags
Just
eNothing
não servem apenas como funções construtoras, mas também como destruidores ou padrões em umacase
expressão. O que significa que, usando esses padrões, você pode ver dentro deMaybe
:Você pode fazer isso, porque o módulo Maybe é definido como
Também poderia dizer
Os dois são equivalentes neste caso, mas ser explícito é considerado uma virtude no Elm, especialmente quando você está escrevendo um pacote.
Ocultando detalhes de implementação
Como apontado acima, é uma escolha deliberada que as funções do construtor
Maybe
sejam visíveis para outros módulos.Há outros casos, porém, em que o autor decide ocultá-los. Um exemplo disso no núcleo é
Dict
. Como consumidor do pacote, você não deve ser capaz de ver os detalhes de implementação do algoritmo de árvore Vermelho / Preto por trásDict
e mexer com os nós diretamente. Ocultar as funções do construtor força o consumidor do seu módulo / pacote a criar apenas valores do seu tipo (e então transformar esses valores) por meio das funções que você expõe.Esta é a razão pela qual às vezes coisas assim aparecem no código
Ao contrário da
type alias
definição no início deste post, esta sintaxe cria um novo tipo de "união" com apenas uma função de construtor, mas essa função de construtor pode ser oculta de outros módulos / pacotes.Se o tipo for exposto assim:
Apenas o código no
Data
módulo pode criar um valor Person e apenas esse código pode corresponder a um padrão nele.fonte
A principal diferença, a meu ver, é se o verificador de tipos gritará com você se você usar o tipo "sinômico".
Crie o seguinte arquivo, coloque-o em algum lugar e execute-o
elm-reactor
, em seguida, vá parahttp://localhost:8000
para ver a diferença:Se você descomentar
2.
e comentar1.
, verá:fonte
Um
alias
é apenas um nome mais curto para algum outro tipo, semelhanteclass
em OOP. Exp:Um
type
(sem alias) vai deixar você definir seu próprio tipo, para que possa definir tipos, comoInt
,String
... para você aplicativo. Por exemplo, em casos comuns, pode ser usado para a descrição de um estado de aplicativo:Assim, você pode manipulá-lo facilmente no
view
olmo:Acho que você sabe a diferença entre
type
etype alias
.Mas por que e como usar
type
etype alias
é importante com oelm
aplicativo, vocês podem consultar o artigo de Josh Claytonfonte