wood west traduccion react meaning bach elm

west - ¿Diferencia en Elm entre tipo y alias de tipo?



elm wood (5)

Cómo lo pienso:

type se usa para definir nuevos tipos de unión:

type Thing = Something | SomethingElse

Antes de esta definición, Something y SomethingElse no significaban nada. Ahora ambos son del tipo Thing , que acabamos de definir.

type alias se utiliza para dar un nombre a otro tipo que ya existe:

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

{ lat = 5, long = 10 } tiene el tipo { lat:Int, long:Int } , que ya era un tipo válido. Pero ahora también podemos decir que tiene el tipo Location porque es un alias para el mismo tipo.

Vale la pena señalar que lo siguiente compilará muy bien y mostrará "thing" . Aunque especificamos thing una thing es una String y aliasedStringIdentity toma una AliasedString , no obtendremos un error de que hay una falta de coincidencia de tipos 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

En Elm, no puedo entender cuándo type es la palabra clave adecuada frente a type alias . La documentación no parece tener una explicación de esto, ni puedo encontrar una en las notas de la versión. ¿Está esto documentado en alguna parte?


La clave es la palabra alias . En el curso de la programación, cuando desea agrupar cosas que pertenecen juntas, lo coloca en un registro, como en el caso de un punto

{ x = 5, y = 4 }

o un registro de estudiante.

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

Ahora, si necesitara pasar estos registros, tendría que deletrear todo el tipo, como:

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

Si pudieras alias un punto, ¡la firma sería mucho más fácil de escribir!

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

Entonces un alias es una abreviatura de otra cosa. Aquí, es una abreviatura para un tipo de registro. Puede considerarlo como un nombre para un tipo de registro que usará con frecuencia. Es por eso que se llama un alias: es otro nombre para el tipo de registro desnudo que está representado por { x:Int, y:Int }

Mientras que el type resuelve un problema diferente. Si viene de OOP, es el problema que resuelve con la herencia, la sobrecarga del operador, etc., a veces, desea tratar los datos como algo genérico, y a veces desea tratarlos como algo específico.

Un lugar común donde sucede esto es cuando se pasan mensajes, como el sistema postal. Cuando envía una carta, desea que el sistema postal trate todos los mensajes como lo mismo, por lo que solo tiene que diseñar el sistema postal una vez. Y además, el trabajo de enrutar el mensaje debe ser independiente del mensaje contenido en él. Solo cuando la carta llega a su destino, le importa cuál es el mensaje.

Del mismo modo, podríamos definir un type como una unión de todos los diferentes tipos de mensajes que podrían ocurrir. Digamos que estamos implementando un sistema de mensajes entre estudiantes universitarios y sus padres. Así que solo hay dos mensajes que los universitarios pueden enviar: "Necesito dinero de cerveza" y "Necesito calzoncillos".

type MessageHome = NeedBeerMoney | NeedUnderpants

Entonces, cuando diseñamos el sistema de enrutamiento, los tipos para nuestras funciones pueden simplemente pasar por MessageHome , en lugar de preocuparse por los diferentes tipos de mensajes que podrían ser. Al sistema de enrutamiento no le importa. Solo necesita saber que es un MessageHome . Solo cuando el mensaje llega a su destino, la casa de los padres, es necesario que descubras de qué se trata.

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

Si conoce la arquitectura Elm, la función de actualización es una declaración de caso gigante, porque ese es el destino de donde se enruta el mensaje y, por lo tanto, se procesa. Y usamos tipos de unión para tener un tipo único con el que lidiar cuando pasamos el mensaje, pero luego podemos usar una declaración de caso para descifrar exactamente qué mensaje era, para que podamos tratarlo.


La principal diferencia, según lo veo, es si el verificador de tipo le gritará si usa el tipo "sinómico".

Cree el siguiente archivo, póngalo en algún lugar y ejecute elm-reactor , luego vaya a http://localhost:8000 para ver la diferencia:

-- 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

Si descomenta 2. y comenta 1. verá:

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


Un alias es solo un nombre más corto para algún otro tipo, class similar en OOP. Exp:

type alias Point = { x : Int , y : Int }

Un type (sin alias) le permitirá definir su propio tipo, para que pueda definir tipos como Int , String , ... para su aplicación. Por ejemplo, en el caso común, se puede usar para la descripción de un estado de la aplicación:

type AppState = Loading --loading state |Loaded --load successful |Error String --Loading error

Para que pueda manejarlo fácilmente en view elm:

-- VIEW ... case appState of Loading -> showSpinner Loaded -> showSuccessData Error error -> showError ...

Creo que sabes la diferencia entre type y type alias .

Pero por qué y cómo usar el type y el type alias es importante con elm aplicación elm , ustedes pueden consultar el artículo de Josh Clayton


Permítanme complementar las respuestas anteriores centrándome en casos de uso y proporcionando un pequeño contexto en las funciones y módulos del constructor.


Usos del type alias

  1. Crear un alias y una función constructora para un registro
    Este es el caso de uso más común: puede definir un nombre alternativo y una función de constructor para un tipo particular de formato de registro.

    type alias Person = { name : String , age : Int }

    La definición del alias de tipo implica automáticamente la siguiente función de constructor (pseudocódigo):
    Person : String -> Int -> { name : String, age : Int }
    Esto puede ser útil, por ejemplo, cuando desea escribir un decodificador Json.

    personDecoder : Json.Decode.Decoder Person personDecoder = Json.Decode.map2 Person (Json.Decode.field "name" Json.Decode.String) (Json.Decode.field "age" Int)


  2. Especificar campos obligatorios
    A veces lo llaman "registros extensibles", que pueden ser engañosos. Esta sintaxis se puede utilizar para especificar que espera algún registro con campos particulares presentes. Como:

    type alias NamedThing x = { x | name : String } showName : NamedThing x -> Html msg showName thing = Html.text thing.name

    Luego puede usar la función anterior de esta manera (por ejemplo, en su vista):

    let joe = { name = "Joe", age = 34 } in showName joe

    La charla de Richard Feldman sobre ElmEurope 2017 puede proporcionar más información sobre cuándo vale la pena usar este estilo.

  3. Renombrar cosas
    Puede hacer esto, porque los nuevos nombres podrían proporcionar un significado adicional más adelante en su código, como en este ejemplo

    type alias Id = String type alias ElapsedTime = Time type SessionStatus = NotStarted | Active Id ElapsedTime | Finished Id

    Quizás un mejor ejemplo de este tipo de uso en core es Time .

  4. Reexponer un tipo desde un módulo diferente
    Si está escribiendo un paquete (no una aplicación), es posible que necesite implementar un tipo en un módulo, quizás en un módulo interno (no expuesto), pero desea exponer el tipo desde un módulo diferente (público). O, alternativamente, desea exponer su tipo de múltiples módulos.
    Task in core y Http.Request en Http son ejemplos para el primero, mientras que el par Json.Encode.Value y Json.Decode.Value es un ejemplo del último.

    Solo puede hacer esto cuando de lo contrario desea mantener el tipo opaco: no expone las funciones del constructor. Para más detalles, consulte los usos del type continuación.

Vale la pena notar que en los ejemplos anteriores solo el # 1 proporciona una función de constructor. Si expone su alias de tipo en el # 1 como module Data exposing (Person) que expondrá el nombre del tipo, así como la función del constructor.



Usos de type

  1. Definir un tipo de unión etiquetado
    Este es el caso de uso más común, un buen ejemplo es el tipo Maybe en el núcleo :

    type Maybe a = Just a | Nothing

    Cuando define un tipo, también define sus funciones de constructor. En caso de Quizás, estos son (pseudocódigo):

    Just : a -> Maybe a Nothing : Maybe a

    Lo que significa que si declaras este valor:

    mayHaveANumber : Maybe Int

    Puedes crearlo ya sea

    mayHaveANumber = Nothing

    o

    mayHaveANumber = Just 5

    Las etiquetas Just y Nothing no solo sirven como funciones de constructor, también sirven como destructores o patrones en una expresión de case . Lo que significa que usando estos patrones puedes ver dentro de un Maybe :

    showValue : Maybe Int -> Html msg showValue mayHaveANumber = case mayHaveANumber of Nothing -> Html.text "N/A" Just number -> Html.text (toString number)

    Puede hacer esto, porque el módulo Quizás se define como

    module Maybe exposing ( Maybe(Just,Nothing)

    También podría decir

    module Maybe exposing ( Maybe(..)

    Los dos son equivalentes en este caso, pero ser explícito se considera una virtud en Elm, especialmente cuando está escribiendo un paquete.


  1. Ocultar detalles de implementación
    Como se señaló anteriormente, es una elección deliberada que las funciones de construcción de Maybe sean visibles para otros módulos.

    Hay otros casos, sin embargo, cuando el autor decide ocultarlos. Un ejemplo de esto en el núcleo es Dict . Como consumidor del paquete, no debería poder ver los detalles de implementación del algoritmo de árbol Rojo / Negro detrás de Dict y meterse directamente con los nodos. Ocultar las funciones del constructor obliga al consumidor de su módulo / paquete a crear solo valores de su tipo (y luego transformar esos valores) a través de las funciones que expone.

    Esta es la razón por la cual a veces cosas como esta aparecen en el código

    type Person = Person { name : String, age : Int }

    A diferencia de la definición de type alias en la parte superior de esta publicación, esta sintaxis crea un nuevo tipo de "unión" con una sola función de constructor, pero esa función de constructor puede ocultarse de otros módulos / paquetes.

    Si el tipo se expone así:

    module Data exposing (Person)

    Solo el código en el módulo de Data puede crear un valor de persona y solo ese código puede coincidir con el patrón.