Elm'de tür ve tür takma adı arasındaki fark?


93

Zaman Elm, ben çözemiyorum typeuygun anahtar kelime vs olduğunu type alias. Belgelerin bununla ilgili bir açıklaması yok gibi görünüyor, ne de sürüm notlarında bir açıklama bulamıyorum. Bu bir yerde belgelendi mi?

Yanıtlar:


136

Nasıl düşünüyorum:

type yeni birleşim türlerini tanımlamak için kullanılır:

type Thing = Something | SomethingElse

Bu tanımdan önce Somethingve SomethingElsehiçbir şey ifade etmiyordu. Şimdi her ikisi de, Thingaz önce tanımladığımız türden .

type alias zaten var olan başka bir türe isim vermek için kullanılır:

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

{ lat = 5, long = 10 }türü olan { lat:Int, long:Int }zaten geçerli bir tür oldu. Ama şimdi Location, aynı tür için bir takma ad olduğu için , türüne sahip olduğunu da söyleyebiliriz .

Aşağıdakilerin iyi bir şekilde derleneceğini ve görüntüleneceğini belirtmekte fayda var "thing". Hatta, belirttiğimiz olsa thingbir olduğunu Stringve aliasedStringIdentitybir sürer AliasedString, biz arasında bir tür uyuşmazlığı olduğunu bir hata almazsınız 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

Son paragrafın amacından emin değilim. Nasıl takma ad verdiğiniz önemli değil, hala aynı tip olduklarını mı söylemeye çalışıyorsunuz?
ZHANG Cheng

8
Evet, derleyicinin takma adı orijinal ile aynı olduğunu
düşündüğünü belirtmek isterim

Yani {}kayıt sözdizimini kullandığınızda, yeni bir tür tanımlıyorsunuz?

3
{ lat:Int, long:Int }yeni bir tür tanımlamaz. Bu zaten geçerli bir tür. type alias Location = { lat:Int, long:Int }ayrıca yeni bir tür tanımlamaz, yalnızca zaten geçerli bir türe başka (belki daha açıklayıcı) bir ad verir. type Location = Geo { lat:Int, long:Int }yeni bir tür tanımlar ( Location)
robertjlooby

1
Tür ve tür takma adı ne zaman kullanılmalıdır? Her zaman yazı kullanmanın dezavantajı nerede?
Richard Haven

8

Anahtar kelime alias. Programlama sırasında, birbirine ait olan şeyleri gruplamak istediğinizde, bir noktada olduğu gibi bir kayda koyarsınız.

{ x = 5, y = 4 }  

veya bir öğrenci kaydı.

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

Şimdi, bu kayıtları başkasına iletmeniz gerekirse, tüm türü yazmanız gerekir, örneğin:

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

Bir noktayı değiştirebilseydiniz, imzayı yazmak çok daha kolay olurdu!

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

Yani takma ad, başka bir şeyin kısaltmasıdır. Burada, bir kayıt türü için bir kısaltmadır. Bunu, sık kullanacağınız bir kayıt türüne bir isim vermek olarak düşünebilirsiniz. Bu yüzden takma ad olarak adlandırılır - temsil edilen çıplak kayıt türü için başka bir addır.{ x:Int, y:Int }

Oysa typefarklı bir sorunu çözer. OOP'den geliyorsanız, kalıtım, operatör aşırı yükleme vb. İle çözdüğünüz problemdir. - bazen veriyi genel bir şey olarak ele almak istersiniz ve bazen ona belirli bir şey gibi davranmak istersiniz.

Bunun olağan bir durumu, posta sistemi gibi mesajların etrafından dolaşılmasıdır. Bir mektup gönderdiğinizde, posta sisteminin tüm mesajları aynı şey olarak ele almasını istersiniz, böylece posta sistemini yalnızca bir kez tasarlamanız gerekir. Ayrıca, mesajı yönlendirme işi, içerdiği mesajdan bağımsız olmalıdır. Sadece mektup hedefine ulaştığında mesajın ne olduğunu umursuyorsunuz.

Aynı şekilde, bir olabilecek typetüm farklı mesaj türlerinin bir birleşimi olarak tanımlayabiliriz . Üniversite öğrencileriyle ebeveynleri arasında bir mesajlaşma sistemi uyguladığımızı varsayalım. Yani üniversite öğrencilerinin gönderebileceği sadece iki mesaj var: 'Bira parasına ihtiyacım var' ve 'külota ihtiyacım var'.

type MessageHome = NeedBeerMoney | NeedUnderpants

Şimdi, yönlendirme sistemini tasarladığımızda, işlevlerimizin MessageHometürleri, olabilecek tüm farklı mesaj türleri hakkında endişelenmek yerine , sadece dolaşabilir. Yönlendirme sistemi umursamıyor. Yalnızca bunun bir MessageHome. Sadece mesaj hedefine, ebeveynin evine ulaştığında ne olduğunu anlamanız gerekir.

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

Elm mimarisini biliyorsanız, güncelleme işlevi dev bir durum ifadesidir, çünkü mesajın yönlendirildiği ve dolayısıyla işlendiği yer burasıdır. Ve mesajı iletirken tek bir türe sahip olmak için birleşim türlerini kullanıyoruz, ancak daha sonra tam olarak hangi mesaj olduğunu anlamak için bir case ifadesi kullanabiliriz, böylece onunla başa çıkabiliriz.


5

Kullanım durumlarına odaklanarak ve yapıcı işlevleri ve modülleri hakkında biraz bağlam sağlayarak önceki yanıtları tamamlayayım.



Kullanımları type alias

  1. Bir kayıt için takma ad ve yapıcı işlevi oluşturma
    Bu en yaygın kullanım durumu: belirli bir kayıt biçimi türü için alternatif bir ad ve yapıcı işlevi tanımlayabilirsiniz.

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

    Tür takma adını tanımlamak, otomatik olarak aşağıdaki yapıcı işlevini (sözde kod) ifade eder:
    Person : String -> Int -> { name : String, age : Int }
    Bu, örneğin bir Json kod çözücü yazmak istediğinizde kullanışlı olabilir.

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


  2. Gerekli alanları belirtin
    Bazen yanıltıcı olabilecek "genişletilebilir kayıtlar" olarak adlandırılırlar. Bu sözdizimi, belirli alanların mevcut olduğu bazı kayıtlar beklediğinizi belirtmek için kullanılabilir. Gibi:

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

    Ardından yukarıdaki işlevi şu şekilde kullanabilirsiniz (örneğin sizin görüşünüzde):

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

    Richard Feldman'ın ElmEurope 2017 hakkındaki konuşması, bu tarzın ne zaman kullanılmaya değer olduğuna dair daha fazla fikir verebilir.

  3. Öğeleri yeniden adlandırma
    Bunu yapabilirsiniz, çünkü bu örnekte olduğu gibi, yeni adlar daha sonra kodunuzda ekstra anlamlar sağlayabilir.

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

    Çekirdekte bu tür kullanımınTime belki de daha iyi bir örneği .

  4. Farklı bir modülden bir türü yeniden açığa çıkarma Bir
    paket yazıyorsanız (bir uygulama değil), bir modülde, belki de dahili (açıkta olmayan) bir modülde bir tür uygulamanız gerekebilir, ancak türü farklı bir (genel) modül. Veya alternatif olarak, türünüzü birden çok modülden ortaya çıkarmak istiyorsunuz.
    Taskin core ve Http içindeki Http.Request ilk örneklerdir, Json.Encode.Value ve Json.Decode.Value çifti ise daha sonraki bir örnektir.

    Bunu yalnızca türü opak tutmak istediğinizde yapabilirsiniz: yapıcı işlevlerini açığa çıkarmazsınız. Ayrıntılar için typeaşağıdaki kullanımlara bakın.

Yukarıdaki örneklerde sadece # 1'in bir yapıcı işlevi sağladığına dikkat etmek önemlidir. Tür takma adınızı # 1'de böyle module Data exposing (Person)gösterirseniz, yapıcı işlevinin yanı sıra tür adını da ortaya çıkarır.



Kullanımları type

  1. Etiketli bir birleşim türü tanımlayın
    Bu en yaygın kullanım durumudur, buna iyi bir örnek, Maybeçekirdekteki türdür :

    type Maybe a
        = Just a
        | Nothing
    

    Bir tür tanımladığınızda, onun yapıcı işlevlerini de tanımlarsınız. Belki durumunda bunlar (sözde kod):

    Just : a -> Maybe a
    
    Nothing : Maybe a
    

    Bu, bu değeri beyan ederseniz:

    mayHaveANumber : Maybe Int
    

    Şununla oluşturabilirsiniz:

    mayHaveANumber = Nothing
    

    veya

    mayHaveANumber = Just 5
    

    JustVe Nothingetiketler sadece onlar da bir de yıkıcılar veya desen olarak hizmet, yapıcı işlevleri olarak hizmet caseifadesi. Bu, bu kalıpları kullanarak aşağıdakileri görebileceğiniz anlamına gelir Maybe:

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

    Bunu yapabilirsiniz, çünkü Belki modülü şöyle tanımlanır:

    module Maybe exposing 
        ( Maybe(Just,Nothing)
    

    Ayrıca diyebilirdi

    module Maybe exposing 
        ( Maybe(..)
    

    Bu durumda ikisi eşdeğerdir, ancak özellikle bir paket yazarken Elm'de açık olmak bir erdem olarak kabul edilir.


  1. Uygulama ayrıntılarını gizlemek
    Yukarıda belirtildiği gibi, yapıcı işlevlerinin Maybediğer modüller için görünür olması bilinçli bir seçimdir .

    Bununla birlikte, yazarın bunları gizlemeye karar verdiği başka durumlar da vardır. Çekirdekte bunun bir örneğiDict . Paketin tüketicisi olarak, arkadaki Kırmızı / Siyah ağaç algoritmasının uygulama detaylarını görememeli Dictve düğümlerle doğrudan uğraşmamalısınız. Yapıcı işlevlerini gizlemek, modülünüzün / paketinizin tüketicisini yalnızca sizin türünüzdeki değerleri yaratmaya (ve sonra bu değerleri, sunduğunuz işlevler aracılığıyla dönüştürmeye) zorlar.

    Bazen bunun gibi şeylerin kodda görünmesinin nedeni budur

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

    Bu yazının üstündeki type aliastanımdan farklı olarak , bu sözdizimi yalnızca bir yapıcı işlevle yeni bir "birleşim" türü oluşturur, ancak bu yapıcı işlevi diğer modüllerden / paketlerden gizlenebilir.

    Tür bu şekilde ortaya çıkarsa:

    module Data exposing (Person)
    

    Yalnızca Datamodüldeki kod bir Kişi değeri oluşturabilir ve yalnızca bu kod bunun üzerinde kalıp eşleşebilir.


1

Gördüğüm kadarıyla temel fark, "eşzamanlı" tip kullanırsanız tip denetleyicisinin size bağırıp çağırmayacağıdır.

Aşağıdaki dosyayı oluşturun, bir yere koyun ve çalıştırın elm-reactor, ardından http://localhost:8000farkı görmek için gidin :

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

Yorum yapmaz 2.ve yorum 1.yaparsanız şunu göreceksiniz:

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

0

An alias, classOOP'de benzer şekilde başka bir tür için sadece daha kısa bir isimdir . Tecrübe:

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

Bir typesen gibi türlerini tanımlamak böylece (takma ad olmadan), kendi türünü tanımlamak izin verir Int, Stringuygulama, ... senin için. Örneğin, yaygın durumda, bir uygulama durumunun açıklaması için kullanılabilir:

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

Böylece viewkaraağaçla kolayca başa çıkabilirsiniz :

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

...

Sanırım typeve arasındaki farkı biliyorsunuz type alias.

Ancak uygulama için neden ve nasıl kullanılacağı typeve type aliasönemli olduğu elmiçin , Josh Clayton'ın makalesine bakabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.