유형 및 유형 별칭 간의 Elm의 차이점은 무엇입니까?


93

Elm에서는 type적절한 키워드와 type alias. 문서에 이에 대한 설명이없는 것 같으며 릴리스 정보에서도 찾을 수 없습니다. 어딘가에 문서화되어 있습니까?

답변:


136

내가 생각하는 방법 :

type 새로운 공용체 유형을 정의하는 데 사용됩니다.

type Thing = Something | SomethingElse

이 정의에 앞서 SomethingSomethingElse평균 아무것도하지 않았다. 이제 둘 다 Thing우리가 정의한 유형 입니다.

type alias 이미 존재하는 다른 유형에 이름을 부여하는 데 사용됩니다.

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

{ lat = 5, long = 10 }유형 { lat:Int, long:Int }이 이미 유효한 유형입니다. 하지만 이제는 Location동일한 유형의 별칭이기 때문에 유형이 있다고 말할 수도 있습니다 .

다음은 잘 컴파일되고 표시된다는 점은 주목할 가치가 있습니다 "thing". 심지어 우리가 지정하지만 thingStringaliasedStringIdentity을 소요 AliasedString, 우리는 사이에 유형 불일치가 있다는 오류가 발생하지 않습니다 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

마지막 단락의 요점에 대해 잘 모르겠습니다. 별칭을 어떻게 지정하더라도 여전히 동일한 유형이라고 말하려고합니까?
ZHANG Cheng

7
예, 바로 컴파일러가 원본과 동일하게 별칭 유형을 고려 지적
robertjlooby

{}레코드 구문 을 사용할 때 새로운 유형을 정의하는 것입니까?

2
{ lat:Int, long:Int }새로운 유형을 정의하지 않습니다. 이미 유효한 유형입니다. type alias Location = { lat:Int, long:Int }또한 새 유형을 정의하지 않고 이미 유효한 유형에 다른 (아마도 더 설명적인) 이름을 제공합니다. type Location = Geo { lat:Int, long:Int }새로운 유형의 (정의 할 Location)
robertjlooby

1
언제 유형과 유형 별칭을 사용해야합니까? 항상 유형을 사용하는 단점은 무엇입니까?
Richard Haven

8

핵심은 단어 alias입니다. 프로그래밍 과정에서 함께 속한 것들을 그룹화하고 싶을 때, 포인트의 경우처럼 레코드에 넣습니다.

{ x = 5, y = 4 }  

또는 학생 기록.

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

이제 이러한 레코드를 전달해야하는 경우 다음과 같이 전체 유형을 철자해야합니다.

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

포인트에 별칭을 지정할 수 있다면 서명을 훨씬 쉽게 작성할 수 있습니다!

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

따라서 별칭은 다른 것에 대한 속기입니다. 여기에서는 레코드 유형의 약어입니다. 자주 사용할 레코드 유형에 이름을 부여하는 것으로 생각할 수 있습니다. 이것이 별칭이라고 불리는 이유입니다. 이는 다음으로 표시되는 네이 키드 레코드 유형의 또 다른 이름입니다.{ x:Int, y:Int }

반면 type다른 문제를 해결합니다. OOP에서 오는 경우 상속, 연산자 오버로딩 등으로 해결하는 문제입니다. 때로는 데이터를 일반적인 것으로 취급하고 싶을 때도 있고, 때로는 특정 것으로 취급하고 싶을 때도 있습니다.

이것이 발생하는 일반적인 경우는 우편 시스템과 같은 메시지를 전달할 때입니다. 편지를 보낼 때 우편 시스템이 모든 메시지를 똑같이 취급하기를 원하므로 우편 시스템을 한 번만 설계하면됩니다. 또한 메시지 라우팅 작업은 포함 된 메시지와 독립적이어야합니다. 편지가 목적지에 도착했을 때만 메시지가 무엇인지 관심이 있습니까?

같은 방식으로, 우리는 발생할 수있는 type모든 다른 유형의 메시지의 결합으로 정의 할 수 있습니다 . 대학생과 부모 사이에 메시징 시스템을 구현한다고 가정 해 보겠습니다. 그래서 대학생들이 보낼 수있는 메시지는 '맥주 돈이 필요해'와 '속옷이 필요해'두 가지뿐입니다.

type MessageHome = NeedBeerMoney | NeedUnderpants

이제 라우팅 시스템을 설계 할 때 함수의 유형이 MessageHome모든 다른 유형의 메시지에 대해 걱정하는 대신 그냥 전달할 수 있습니다. 라우팅 시스템은 상관하지 않습니다. 그것이 MessageHome. 메시지가 목적지 인 부모의 집에 도착했을 때만 메시지가 무엇인지 알아 내야합니다.

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

Elm 아키텍처를 알고있는 경우 업데이트 함수는 메시지가 라우팅되고 처리되는 대상이기 때문에 거대한 case 문입니다. 그리고 유니온 타입을 사용하여 메시지를 전달할 때 처리 할 단일 타입을 가지지 만 case 문을 사용하여 정확히 어떤 메시지인지 알아낼 수 있으므로 처리 할 수 ​​있습니다.


5

사용 사례에 초점을 맞추고 생성자 함수 및 모듈에 대한 약간의 컨텍스트를 제공하여 이전 답변을 보완하겠습니다.



용도 type alias

  1. 레코드에 대한 별칭 및 생성자 함수 만들기
    가장 일반적인 사용 사례 : 특정 종류의 레코드 형식에 대한 대체 이름 및 생성자 함수를 정의 할 수 있습니다.

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

    타입 별칭을 정의하는 것은 자동으로 다음 생성자 함수 (의사 코드)를 의미합니다.
    Person : String -> Int -> { name : String, age : Int }
    예를 들어 Json 디코더를 작성하려는 경우 유용 할 수 있습니다.

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


  2. 필수 필드 지정
    때때로 "확장 가능한 레코드"라고 부르는데 이는 오해의 소지가 있습니다. 이 구문은 특정 필드가있는 일부 레코드를 예상하도록 지정하는 데 사용할 수 있습니다. 예 :

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

    그런 다음 위의 함수를 다음과 같이 사용할 수 있습니다 (예 :보기에서).

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

    ElmEurope 2017에 대한 Richard Feldman의 강연은 이 스타일을 사용할 가치가있는시기에 대한 추가 통찰력을 제공 할 수 있습니다.

  3. 물건 이름 바꾸기
    새로운 이름이 예처럼, 나중에 코드에서에 여분의 의미를 제공 할 수 있기 때문에, 당신은이 작업을 수행 할 수 있습니다를

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

    코어에서 이러한 종류의 사용에Time 대한 더 나은 예 .

  4. 다른 모듈에서 유형 다시 노출
    패키지 (애플리케이션이 아님)를 작성하는 경우 한 모듈, 아마도 내부 (노출되지 않음) 모듈에서 유형을 구현해야 할 수 있지만 유형을 노출하려는 경우 다른 (공용) 모듈. 또는 여러 모듈에서 유형을 노출하려고합니다.
    Taskin core 및 HttpHttp.Request 가 첫 번째 예 이고 Json.Encode.ValueJson.Decode.Value 쌍이 나중의 예입니다.

    유형을 불투명하게 유지하려는 경우에만이 작업을 수행 할 수 있습니다. 생성자 함수를 노출하지 않습니다. 자세한 내용은 type아래의 사용법을 참조하십시오 .

위의 예에서 # 1 만 생성자 함수를 제공한다는 점은 주목할 가치가 있습니다. 형식 별칭을 # 1에서 module Data exposing (Person)노출하면 형식 이름과 생성자 함수가 노출됩니다.



용도 type

  1. 태그가 지정된 공용체 유형 정의
    이것은 가장 일반적인 사용 사례이며, 좋은 예는 Maybecore유형입니다 .

    type Maybe a
        = Just a
        | Nothing

    유형을 정의 할 때 생성자 함수도 정의합니다. Maybe의 경우 다음은 (의사 코드)입니다.

    Just : a -> Maybe a
    
    Nothing : Maybe a

    이는이 값을 선언하면 다음을 의미합니다.

    mayHaveANumber : Maybe Int

    다음 중 하나를 사용하여 만들 수 있습니다.

    mayHaveANumber = Nothing

    또는

    mayHaveANumber = Just 5

    JustNothing태그뿐만 아니라 그들은 또한에 소멸자 또는 패턴의 역할, 생성자 함수의 역할을 case표현. 즉, 이러한 패턴을 사용하면 내부에서 볼 수 있습니다 Maybe.

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

    Maybe 모듈이 다음과 같이 정의되어 있기 때문에 이렇게 할 수 있습니다.

    module Maybe exposing 
        ( Maybe(Just,Nothing)

    그것은 또한 말할 수 있습니다

    module Maybe exposing 
        ( Maybe(..)

    이 경우 두 가지는 동일하지만, 특히 패키지를 작성할 때 명시적인 것은 Elm에서 미덕으로 간주됩니다.


  1. 구현 세부 정보 숨기기
    위에서 지적했듯이의 생성자 함수가 Maybe다른 모듈에 대해 표시 되도록하는 것은 신중한 선택입니다 .

    그러나 저자가 숨기기로 결정한 다른 경우가 있습니다. 핵심에서 이것의 한 가지 예는Dict . 패키지의 소비자로서 Red / Black 트리 알고리즘의 구현 세부 사항을 볼 수 없어서 Dict노드를 직접 엉망으로 만들 수 없습니다. 생성자 함수를 숨기면 모듈 / 패키지의 소비자가 노출하는 함수를 통해 유형의 값만 생성 한 다음 해당 값을 변환하게됩니다.

    이것이 때때로 이와 같은 것이 코드에 나타나는 이유입니다.

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

    type alias이 게시물의 맨 위에 있는 정의 와 달리이 구문은 생성자 함수가 하나만있는 새로운 "union"유형을 생성하지만 해당 생성자 함수는 다른 모듈 / 패키지에서 숨길 수 있습니다.

    유형이 다음과 같이 노출되는 경우 :

    module Data exposing (Person)

    Data모듈의 코드 만 Person 값을 생성 할 수 있으며 해당 코드 만 패턴 일치를 생성 할 수 있습니다.


1

내가보기에 가장 큰 차이점은 "synomical"유형을 사용하면 유형 검사기가 당신에게 소리를 지르는 지 여부입니다.

다음 파일을 만들고 어딘가에 넣고를 실행 elm-reactor한 다음로 이동 http://localhost:8000하여 차이점을 확인하십시오.

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

주석을 제거 2.하고 주석 을다는 경우 다음 1.이 표시됩니다.

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 aliasclassOOP 와 유사한 다른 유형의 짧은 이름입니다 . 특급 :

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

type당신 같은 타입을 정의 할 수 있습니다 (별칭없이)는, 자신의 유형을 정의 할 것 Int, String앱 ... 당신을 위해. 예를 들어, 일반적인 경우 앱 상태 설명에 사용할 수 있습니다.

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

따라서 viewelm 에서 쉽게 처리 할 수 ​​있습니다 .

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

...

type과 의 차이점을 알고 계신 것 같습니다 type alias.

하지만 왜 그리고 어떻게 사용 type하고 앱에서 type alias중요한지 elm, 여러분 은 Josh Clayton기사를 참조 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.