2015-08-24 13 views
77

In Elm, non riesco a capire quando type è la parola chiave appropriata rispetto a type alias. La documentazione non sembra avere una spiegazione di ciò, né posso trovarne una nelle note di rilascio. Questo è documentato da qualche parte?Differenza tra Elm e tipo alias?

risposta

117

Come ci penso:

type è usato per definire nuovi tipi di unione:

type Thing = Something | SomethingElse 

Prima di questa definizione Something e SomethingElse non significava nulla. Ora sono entrambi di tipo Thing, che abbiamo appena definito.

type alias è usato per dare un nome a qualche altro tipo che già esiste:

type alias Location = { lat:Int, long:Int } 

{ lat = 5, long = 10 } è di tipo { lat:Int, long:Int }, che era già un tipo valido. Ma ora possiamo anche dire che ha tipo Location perché è un alias per lo stesso tipo.

Vale la pena notare che quanto segue si compila bene e visualizza "thing". Anche se specifichiamo thing è un String e aliasedStringIdentity prende un AliasedString, non otteniamo un errore che ci sia un tipo di mancata corrispondenza tra 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 
+0

sicuro del tuo punto dell'ultimo paragrafo. Stai cercando di dire che sono sempre dello stesso tipo, non importa come lo si alias? –

+6

Sì, solo sottolineando che il compilatore considera il tipo di alias essere uguale all'originale – robertjlooby

+0

Quindi quando si utilizza la sintassi del record '{}', si sta definendo un nuovo tipo? –

6

La chiave è la parola alias. Nel corso della programmazione, quando si vuole le cose che appartengono gruppo insieme, lo metti in un record, come nel caso di un punto

{ x = 5, y = 4 } 

o un record di studente.

{ name = "Billy Bob", grade = 10, classof = 1998 } 

Ora, se è necessario passare questi record in giro, che avrebbe dovuto precisare l'intero tipo, come:

add : { x:Int, y:Int } -> { x:Int, y:Int } -> { x:Int, y:Int } 
add a b = 
    { a.x + b.x, a.y + b.y } 

Se si potesse alias un punto, la firma sarebbe così molto più facile da scrivere!

type alias Point = { x:Int, y:Int } 
add : Point -> Point -> Point 
add a b = 
    { a.x + b.x, a.y + b.y } 

Quindi un alias è una scorciatoia per qualcos'altro. Qui, è una scorciatoia per un tipo di record. Puoi pensare a come dare un nome a un tipo di record che utilizzerai spesso. Ecco perché si chiama alias: è un altro nome per il tipo di record nudo rappresentato da { x:Int, y:Int }

considerando che type risolve un problema diverso. Se provieni da OOP, è il problema che risolvi con ereditarietà, sovraccarico dell'operatore, ecc .-- a volte, vuoi trattare i dati come una cosa generica, ea volte vuoi trattarli come una cosa specifica.

Un luogo comune in cui ciò accade è quando si passano messaggi come il sistema postale. Quando invii una lettera, vuoi che il sistema postale tratti tutti i messaggi come la stessa cosa, quindi devi solo progettare il sistema postale una volta. E inoltre, il compito di instradare il messaggio dovrebbe essere indipendente dal messaggio contenuto all'interno.È solo quando la lettera raggiunge la sua destinazione, ti interessa ciò che il messaggio è.

Allo stesso modo, potremmo definire uno type come unione di tutti i diversi tipi di messaggi che potrebbero accadere. Diciamo che stiamo implementando un sistema di messaggistica tra studenti universitari e genitori. Quindi ci sono solo due messaggi che i ragazzi del college possono inviare: "Ho bisogno di soldi per la birra" e "Ho bisogno di mutande".

type MessageHome = NeedBeerMoney | NeedUnderpants 

Così ora, quando progettiamo il sistema di routing, i tipi per le nostre funzioni può semplicemente passare intorno MessageHome, invece di preoccuparsi di tutti i diversi tipi di messaggi potrebbe essere. Il sistema di routing non interessa. Basta sapere che è un MessageHome. È solo quando il messaggio raggiunge la sua destinazione, la casa dei genitori, che è necessario capire di cosa si tratta.

case message of 
    NeedBeerMoney -> 
    sayNo() 
    NeedUnderpants -> 
    sendUnderpants(3) 

Se si conosce l'architettura Elm, la funzione di aggiornamento è una dichiarazione caso gigante, perché questa è la destinazione dove il messaggio viene instradato, e quindi elaborati. E usiamo i tipi di unione per avere un singolo tipo da trattare quando si passa il messaggio in giro, ma poi possiamo usare una dichiarazione del caso per capire esattamente quale messaggio fosse, quindi possiamo gestirlo.

1

La differenza principale, a mio avviso, è se il tipo di controllo vi urlerà se si utilizza il tipo "synomical".

Creare il seguente file, metterlo da qualche parte ed eseguire elm-reactor, poi vai a http://localhost:8000 per vedere la differenza:

-- Boilerplate code 

module Main exposing (main) 

import Html exposing (..) 

main = 
    Html.beginnerProgram 
    { 
     model = identity, 
     view = view, 
     update = identity 
    } 

-- Our type system 

type alias IntRecordAlias = {x : Int} 
type IntRecordType = 
    IntRecordType {x : Int} 

inc : {x : Int} -> {x : Int} 
inc r = {r | x = .x r + 1} 

view model = 
    let 
    -- 1. This will work 
    r : IntRecordAlias 
    r = {x = 1} 

    -- 2. However, this won't work 
    -- r : IntRecordType 
    -- r = IntRecordType {x = 1} 
    in 
    Html.text <| toString <| inc r 

Se decommentare 2. e commentare 1. si vedrà:

The argument to function `inc` is causing a mismatch. 

34|        inc r 
            ^
Function `inc` is expecting the argument to be: 

    { x : Int } 

But it is: 

    IntRecordType 
Non
Problemi correlati