Diferencia en Elm entre tipo y alias de tipo?
En Elm, no puedo averiguar cuándo type
es la palabra clave apropiada vs. 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á documentado en alguna parte?
4 answers
Cómo lo pienso: {[19]]}
type
se utiliza para definir nuevos tipos de unión:
type Thing = Something | SomethingElse
Antes de esta definición Something
y SomethingElse
no significaba nada. Ahora ambos son de tipo Thing
, que acabamos de definir.
type alias
se utiliza para dar un nombre a algún 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"
. A pesar de que especificamos thing
es un String
y aliasedStringIdentity
toma un AliasedString
, no obtendremos un error de que hay un desajuste 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-29 16:35:01
La clave es La palabra alias
. En el curso de la programación, cuando quieres agrupar cosas que pertenecen juntas, lo pones en un registro, como en el caso de un punto
{ x = 5, y = 4 }
O un registro del estudiante.
{ name = "Billy Bob", grade = 10, classof = 1998 }
Ahora, si usted necesita pasar estos registros alrededor, usted 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 pudiera 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 }
Así que un alias es una abreviatura de otra cosa. Aqui, es una taquigrafía para un tipo de disco. Usted puede pensar en ello como dar un nombre a un tipo de registro que va a utilizar a menudo. Es por eso que se llama un alias's es otro nombre para el tipo de registro desnudo que está representado por { x:Int, y:Int }
, Mientras que type
resuelve un problema diferente. Si vienes de OOP, es el problema que resuelves con la herencia,la sobrecarga del operador, etc.-- a veces, quieres tratar los datos como algo genérico, y a veces quieres tratarlos como algo específico cosa.
Un lugar común donde esto sucede es cuando se pasan mensajes around como el sistema postal. Cuando envías una carta, quieres que el sistema postal trate todos los mensajes como la misma cosa, por lo que solo tienes que diseñar el sistema postal una vez. Y además, el trabajo de enrutar el mensaje debe ser independiente del mensaje contenido dentro. Solo cuando la carta llega a su destino te importa cuál es el mensaje.
De la misma manera, podríamos definir un type
como una unión de todos los diferentes tipos de mensajes que podrían suceder. Digamos que estamos implementando un sistema de mensajería entre estudiantes universitarios a sus padres. Así que solo hay dos mensajes que los universitarios pueden enviar: 'Necesito dinero para cerveza' y 'Necesito calzoncillos'.
type MessageHome = NeedBeerMoney | NeedUnderpants
Así que ahora, cuando diseñamos el sistema de enrutamiento, los tipos de nuestras funciones solo pueden pasar MessageHome
, en lugar de preocuparse por todos los diferentes tipos de mensajes que podría ser. Al sistema de enrutamiento no le importa. Sólo necesita sepa que es un MessageHome
. Es solo cuando el mensaje llega a su destino, la casa de los padres, que necesitas averiguar qué es.
case message of
NeedBeerMoney ->
sayNo()
NeedUnderpants ->
sendUnderpants(3)
Si conoce la arquitectura Elm, la función update es una sentencia case gigante, porque ese es el destino de donde se enruta el mensaje y, por lo tanto, se procesa. Y usamos los tipos de unión para tener un solo tipo con el que lidiar al pasar el mensaje, pero luego podemos usar una sentencia case para desentrañar exactamente qué mensaje fue, para que podamos acéptalo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-13 03:24:10
La principal diferencia, como yo lo veo, es si type checker le gritará si usa el tipo "synomical".
Cree el siguiente archivo, colóquelo 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 descomentas 2.
y comentas 1.
verás:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-07 07:09:44
Permítanme complementar las respuestas anteriores centrándose en los casos de uso y proporcionando un poco de contexto sobre las funciones y módulos del constructor.
Usos de type alias
-
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 }
Definir el alias de tipo automáticamente implica la siguiente función constructora (pseudo có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)
-
Especifique los campos obligatorios
A veces lo llaman "registros extensibles", lo que puede ser engañoso. Esta sintaxis se puede usar para especificar que está esperando algún registro con campos particulares presentes. Tales como:type alias NamedThing x = { x | name : String } showName : NamedThing x -> Html msg showName thing = Html.text thing.name
Entonces puede usar la función anterior de esta manera (para 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.
-
Cambiar el nombre de cosas
Usted podría hacer esto, porque los nuevos nombres podrían proporcionar un significado adicional más adelante en su código, como en este ejemplotype 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
. -
Volver a exponer 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, tal vez 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 desde varios módulos.Task
en core y Http.Petición en Http son ejemplos para la primera, mientras que the Json.Codificar.Valor y Json.Decodificar.El par Value es un ejemplo del último.Solo puede hacer esto cuando desea mantener el tipo opaco: no expone las funciones del constructor. Para obtener más detalles, consulte los usos de
type
a continuación.
Vale la pena notar que en los ejemplos anteriores solo #1 proporciona una función de constructor. Si expone su alias de tipo en # 1 como module Data exposing (Person)
eso expondrá el nombre del tipo, así como el constructor función.
Usos de type
-
Definir un tipo de unión etiquetado
Este es el caso de uso más común, un buen ejemplo de ello es elMaybe
escriba el núcleo :type Maybe a = Just a | Nothing
Cuando define un tipo, también define sus funciones constructoras. En caso de Tal vez estos son (pseudo-código):
Just : a -> Maybe a Nothing : Maybe a
Lo que significa que si declaras este valor:
mayHaveANumber : Maybe Int
Puede crearlo por cualquiera de los dos
mayHaveANumber = Nothing
O
mayHaveANumber = Just 5
Las etiquetas
Just
yNothing
no solo sirven como funciones constructoras, también sirven como destructores o patrones en una expresióncase
. Lo que significa que usando estos patrones se puede ver dentro de unMaybe
:showValue : Maybe Int -> Html msg showValue mayHaveANumber = case mayHaveANumber of Nothing -> Html.text "N/A" Just number -> Html.text (toString number)
Puedes hacer esto, porque el módulo Maybe está definido como
module Maybe exposing ( Maybe(Just,Nothing)
También podría decir
module Maybe exposing ( Maybe(..)
Las dos son equivalentes en este caso, pero ser explícito se considera una virtud en Elm, especialmente cuando se escribiendo un paquete.
-
Ocultar detalles de implementación
Como se señaló anteriormente, es una elección deliberada que las funciones constructoras deMaybe
sean visibles para otros módulos.Hay otros casos, sin embargo, cuando el autor decide ocultarlos. Un ejemplo de esto en core es
Dict
. Como consumidor del paquete, no debería poder ver los detalles de implementación del árbol Rojo/Negro algoritmo detrás deDict
y meterse con los nodos directamente. 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 que a veces cosas como esta aparecen en el código
type Person = Person { name : String, age : Int }
A diferencia de la definición
type alias
en la parte superior de este post, esta sintaxis crea un nuevo tipo "union" con solo una función constructora, pero esa función constructora puede estar oculto de otros módulos / paquetes.Si el tipo se expone así:
module Data exposing (Person)
Solo el código en el módulo
Data
puede crear un valor de Persona y solo ese código puede coincidir con el patrón.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-29 14:51:43