The difference in knitting between type and type alias?

In Elm, I can't figure out when type is the corresponding keyword vs type alias . There seems to be no explanation in the documentation, and I cannot find it in the release notes. Is it documented anywhere?

+89
elm
Aug 24 '15 at 16:48
source share
5 answers

How I think about it:

type used to define new connection types:

 type Thing = Something | SomethingElse 

Prior to this definition, Something and SomethingElse meant nothing. Now they are both the types of Thing that we just defined.

type alias used to name another type that already exists:

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

{ lat = 5, long = 10 } is of type { lat:Int, long:Int } , which was already a valid type. But now we can also say that it is of type Location , because it is an alias for the same type.

It is worth noting that the following will compile just fine and display the "thing" . Even if we specify Thing , it is String and aliasedStringIdentity accepts AliasedString , we will not get an error that there is a type mismatch between 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 
+131
Aug 24 '15 at 18:08
source share

The key is the word alias . In the programming process, when you want to group things that belong together, you put them in a record, for example, in the case of a period

 { x = 5, y = 4 } 

or student record.

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

Now, if you need to pass these records, you will need to specify the entire type, for example:

 add : { x:Int, y:Int } -> { x:Int, y:Int } -> { x:Int, y:Int } add ab = { ax + bx, ay + by } 

If you could pseudonize the point, the signature would be much easier to write!

 type alias Point = { x:Int, y:Int } add : Point -> Point -> Point add ab = { ax + bx, ay + by } 

So an alias is a shorthand for something else. This is an abbreviation for the record type here. You can think of it as the name of a post type that you will often use. That's why he called an alias - it's a different name for the bare record type represented by { x:Int, y:Int }

While type solves another problem. If you came from OOP, this is a problem that you solve with inheritance, operator overloading, etc. - sometimes you want to process data as a general thing, and sometimes you want to treat it as a certain thing.

A common occurrence when this happens is messaging - like a mail system. When you send an email, you want the mail system to process all messages as the same, so you only need to create the mail system once. And besides, the message routing task should be independent of the message contained inside. It is only when the letter reaches its destination that you care about what the message is.

In the same way, we could define a type as the union of all the different types of messages that can occur. Say we are introducing a messaging system between college students with parents. Thus, there are only two messages that college kids can send: "I need beer money" and "I need cowards."

 type MessageHome = NeedBeerMoney | NeedUnderpants 

So, when we design a routing system, the types for our functions can just go through the MessageHome , rather than worry about all the different types of messages that may be. The routing system does not care. Only MessageHome needs to know this. It is only when the message reaches its addressee, the parental home, that you need to find out what it is.

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

If you know the Elm architecture, the update function is a gigantic case argument because it is the destination where the message is routed and therefore processed. And we use union types to have one type that we have to deal with when sending a message, but then he can use the case statement to tease exactly what it was, so we can deal with it.

+7
Jul 18 '17 at 19:23
source share

Let me complement the previous answers by focusing on usage scenarios and providing a little context on constructor functions and modules.





Using type alias

  1. Create an alias and constructor function for writing
    This is the most common use case: you can define an alternate name and constructor function for a particular type of recording format.

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

    The definition of a type alias automatically implies the following constructor function (pseudocode):
    Person: String β†’ Int β†’ { name: String, age: Int }
    This may come in handy, for example, when you want to write a Json decoder.

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


  2. Indicate required fields
    Sometimes they call it β€œexpandable entries,” which can be misleading. This syntax can be used to indicate that you are expecting some kind of record with specific fields. Such as:

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

    Then you can use the above function, for example, like this (for example, in your opinion):

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

    Speech by Richard Feldman at ElmEurope 2017 may give an additional idea of ​​when to use this style.

  3. Rename Material
    You can do this because the new names may give additional meaning later in your code, as in this example

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

    Perhaps the best example of such use in the kernel is Time .

  4. Overexposing a type from another module
    If you are writing a package (not an application), you may need to implement the type in one module, perhaps in an internal (not provided) module, but you want to provide a type from another (open) module. Or, alternatively, you want to set your type from several modules.
    Task core task and Http.Request in Http are examples for the former, while the pair of Json.Encode.Value and Json.Decode.Value are examples of the latter .

    You can only do this when you want to keep the type opaque: you do not provide constructor functions. For more information, see Using type below.

It is worth noting that in the above examples, only # 1 provides a constructor function. If you set your type alias to # 1, for example, in module Data exposing (Person) , which will represent both the type name and the constructor function.



Using type

  1. Define a marked union type
    This is the most common use case, a good example of which is the Maybe type in the kernel :

     type Maybe a = Just a | Nothing 

    When you define a type, you also define its constructor functions. In the case of Maybe, this is (pseudocode):

     Just : a -> Maybe a Nothing : Maybe a 

    This means that if you declare this value:

     mayHaveANumber : Maybe Int 

    You can create it either

     mayHaveANumber = Nothing 

    or

     mayHaveANumber = Just 5 

    The Just and Nothing tags not only serve as constructor functions, they also serve as destructors or templates in the case expression. This means that with these templates you can see inside Maybe :

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

    You can do this because Maybe module is defined as

     module Maybe exposing ( Maybe(Just,Nothing) 

    You can also say

     module Maybe exposing ( Maybe(..) 

    In this case, they are equivalent, but the obvious is considered an advantage in Elm, especially when you write a package.


  1. Concealment of implementation details
    As stated above, this is a deliberate choice to make Maybe constructor functions visible to other modules.

    However, there are other cases when the author decides to hide them. One example of this is mainly Dict . As a user of the package, you should not see the implementation details of the Red / Black tree algorithm behind the Dict and directly communicate with the nodes. Hiding constructor functions forces the consumer of your module / package to create only your type values ​​(and then convert these values) using the functions you expose.

    This is the reason why such things sometimes appear in code.

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

    Unlike the definition of type alias at the beginning of this post, this syntax creates a new type of "union" with only one constructor function, but this constructor function can be hidden from other modules / packages.

    If the type is set like this:

     module Data exposing (Person) 

    Only code in the Data module can create a Person value, and only this code can match a pattern.

+4
May 29 '18 at 14:51
source share

The main difference, as I see it, is that the type will scream at you if you use the "synonymous" type.

Create the following file, place it somewhere and run elm-reactor , then go to http://localhost:8000 to see the difference:

 -- 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 = .xr + 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 

If you uncomment 2. and comment 1. , you will see:

 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 
+1
Nov 07 '17 at 7:04 on
source share

alias is just a shorter name for some other type, similar to class in OOP. Experience:

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

type (without an alias) allows you to define your own type so that you can define types such as Int , String , ... for your application. For example, in general, it can use to describe the state of an application:

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

So you can handle view elm:

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

I think you know the difference between type and type alias .

But why and how to use type and type alias is important with the elm application, you guys can refer to the article by Josh Clayton

0
Apr 16 '19 at 4:46
source share



All Articles