Elm Language
유형, 유형 변수 및 유형 생성자
수색…
비고
이 개념들을 가지고 직접 놀아주세요! elm-repl ( REPL 입문 참조)은 위의 코드로 놀아 볼 수있는 좋은 곳입니다. elm-repl online으로 온라인 게임을 즐길 수도 있습니다 .
비교 가능한 데이터 유형
비교 가능한 유형은 기본 모듈의 비교 연산자를 사용하여 비교할 수있는 기본 유형입니다 (<) 예 : (<) , (>) , (<=) , (>=) , max , min , compare
Elm의 비교 가능한 유형은 Int , Float , Time , Char , String 및 유사한 유형의 튜플 또는 목록입니다.
문서 또는 유형 정의에서 그들은 변수 특별한 유형으로 불린다 comparable 예. Basics.max 함수의 유형 정의를 참조하십시오.
max : comparable -> comparable -> comparable
유형 서명
느릅 나무에서 값은 이름, 등호 및 실제 값을 쓰면 선언됩니다.
someValue = 42
함수는 값이거나 값을 인수로 추가하는 값입니다. 보통 다음과 같이 작성됩니다.
double n = n * 2
Elm의 모든 값에는 유형이 있습니다. 위의 값 유형은 사용 방법에 따라 컴파일러에서 유추 됩니다. 그러나 최상위 값의 유형을 항상 명시 적으로 선언하고 다음과 같이 형식 시그니처 를 작성하는 것이 가장 좋습니다.
someValue : Int
someValue =
42
someOtherValue : Float
someOtherValue =
42
우리가 볼 수 있듯이, 42 양자 택일로 정의 할 수 있습니다 Int 또는 Float . 직관적 인 의미를 갖지만 자세한 내용은 유형 변수 를 참조하십시오.
형식 시그니처는 함수와 함께 사용할 때 특히 유용합니다. 이전의 배가 함수는 다음과 같습니다.
double : Int -> Int
double n =
n * 2
이번에는 서명에 -> 기호가 있고, 서명을 "int에서 int"로 발음하거나, "정수를 취하여 정수를 반환합니다." -> 줌으로써을 나타냅니다 double Int 인수로 값을 double 돌아갑니다 Int . 따라서 정수에 정수가 걸립니다.
> double
<function> : Int -> Int
> double 3
6 : Int
기본 유형
elm-repl 에 코드 값을 입력하여 값과 유추 된 유형을 가져옵니다. 다음을 시도하여 존재하는 다양한 유형에 대해 알아보십시오.
> 42
42 : number
> 1.987
1.987 : Float
> 42 / 2
21 : Float
> 42 % 2
0 : Int
> 'e'
'e' : Char
> "e"
"e" : String
> "Hello Friend"
"Hello Friend" : String
> ['w', 'o', 'a', 'h']
['w', 'o', 'a', 'h'] : List Char
> ("hey", 42.42, ['n', 'o'])
("hey", 42.42, ['n', 'o']) : ( String, Float, List Char )
> (1, 2.1, 3, 4.3, 'c')
(1,2.1,3,4.3,'c') : ( number, Float, number', Float, Char )
> {}
{} : {}
> { hey = "Hi", someNumber = 43 }
{ hey = "Hi", someNumber = 43 } : { hey : String, someNumber : number }
> ()
() : ()
{} 는 빈 레코드 유형이고 () 는 빈 터플 유형입니다. 후자는 종종 게으른 평가 목적으로 사용됩니다. 함수 및 부분 응용 프로그램 의 해당 예제를 참조하십시오.
number 가 어떻게 자본화되지 않은 것처럼 보입니까? 이것은 이것이 유형 변수 임을 나타내며, 또한 특정 단어 number 는 Int 또는 Float 일 수있는 특수 유형 변수 를 나타냅니다 (자세한 내용은 해당 절 참조). 형식은 Char , Float , List String 등과 같이 항상 대문자입니다.
유형 변수
타입 변수는 타입 시그니처의 비 구 체화 된 이름입니다. Int 및 String 과 같은 대문자와는 달리 단일 유형이 아니라 모든 유형을 나타냅니다. 이 함수는 모든 유형 또는 유형에서 작동 할 수있는 제네릭 함수를 작성하는 데 사용되며 List 또는 Dict 와 같은 컨테이너에 연산을 작성하는 데 특히 유용합니다. 예를 들어, List.reverse 함수에는 다음과 같은 서명이 있습니다.
reverse : List a -> List a
어떤는 모든 유형의 값 목록에서 작업 할 수 있습니다 의미하므로 List Int , List (List String) , 그 어떤 다른 사람의 모두 할 수 있습니다 reversed 모두 같은. 따라서 a 는 모든 유형에 대해 참을 수있는 유형 변수입니다.
reverse 함수는 number 와 같은 소수의 특수한 변수 이름을 제외하고는 형식 시그니처에 대문자 사용 되지 않은 변수 이름을 사용할 수있었습니다 (자세한 내용은 해당 예제를 참조하십시오).
reverse : List lol -> List lol
reverse : List wakaFlaka -> List wakaFlaka
유형 변수의 이름은 단일 서명 내에 다른 유형 변수가있을 때만 의미가 있습니다 (예 : 목록의 map 기능).
map : (a -> b) -> List a -> List b
map 임의의 타입의 일부 기능을 얻어 어떤 유형에 a b 일부 유형의 요소 목록과 함께 , 일부 타입의 엘리먼트들의리스트를 반환 a b 가 목록의 각 요소에 소정의 함수를 적용함으로써 얻는다.
서명을 더 자세히 보도록하자.
plusOne : Int -> Int
plusOne x =
x + 1
> List.map plusOne
<function> : List Int -> List Int
우리는 모두 볼 수 있듯이 a = Int 및 b = Int 이 경우. 하지만, map 와 같은 유형 서명했다 map : (a -> a) -> List a -> List a , 그것은 단지 하나의 유형에서 작동 기능에 작동합니다, 당신은 변경할 수 없을 것 map 함수를 사용하여 목록의 유형을 지정합니다. 그러나 map 의 타입 시그니처에는 여러 가지 다른 유형 변수 인 a 와 b 가 있으므로 map 을 사용하여리스트 유형을 변경할 수 있습니다.
isOdd : Int -> Bool
isOdd x =
x % 2 /= 0
> List.map isOdd
<function> : List Int -> List Bool
이 경우 a = Int 및 b = Bool 입니다. 따라서 다른 유형을 사용하고 반환 할 수있는 함수를 사용하려면 다른 유형 변수를 사용해야합니다.
유형 별칭
때로는 좀 더 구체적인 이름을 입력하기를 원합니다. Google 앱에 사용자를 나타내는 데이터 유형이 있다고 가정 해 보겠습니다.
{ name : String, age : Int, email : String }
그리고 사용자에 대한 우리의 함수는 다음 행을 따라 타입 시그니처를가집니다.
prettyPrintUser : { name : String, age : Int, email : String } -> String
이것은 사용자가 더 큰 레코드 유형을 사용하는 경우 상당히 다루기 힘들어 질 수 있으므로 유형 별칭 을 사용하여 크기를 줄이고 해당 데이터 구조에보다 의미있는 이름을 지정합니다.
type alias User =
{ name: String
, age : Int
, email : String
}
prettyPrintUser : User -> String
유형 별칭을 사용하면 응용 프로그램에 대한 모델을 정의하고 사용할 수 있습니다.
type alias Model =
{ count : Int
, lastEditMade : Time
}
type alias 사용하면 문자 그대로 type alias 붙입니다. 위의 Model 유형을 사용하는 것은 { count : Int, lastEditMade : Time } 을 사용하는 것과 정확히 동일합니다. 다음은 별칭이 기본 유형과 다른 점을 보여주는 예입니다.
type alias Bugatti = Int
type alias Fugazi = Int
unstoppableForceImmovableObject : Bugatti -> Fugazi -> Int
unstoppableForceImmovableObject bug fug =
bug + fug
> unstoppableForceImmovableObject 09 87
96 : Int
레코드 유형에 대한 유형 별명은 선언 순서대로 각 필드에 하나의 인수가있는 A 스트 0 터 함수를 정의합니다.
type alias Point = { x : Int, y : Int }
Point 3 7
{ x = 3, y = 7 } : Point
type alias Person = { last : String, middle : String, first : String }
Person "McNameface" "M" "Namey"
{ last = "McNameface", middle = "M", first = "Namey" } : Person
각 레코드 유형 별명은 호환 가능한 유형의 경우에도 자체 필드 순서를 갖습니다.
type alias Person = { last : String, middle : String, first : String }
type alias Person2 = { first : String, last : String, middle : String }
Person2 "Theodore" "Roosevelt" "-"
{ first = "Theodore", last = "Roosevelt", middle = "-" } : Person2
a = [ Person "Last" "Middle" "First", Person2 "First" "Last" "Middle" ]
[{ last = "Last", middle = "Middle", first = "First" },{ first = "First", last = "Last", middle = "Middle" }] : List Person2
새로운 유형을 사용하여 유형 안전 향상
앨리어싱 유형은 상용구를 줄이고 가독성을 향상 시키지만 별칭이 지정된 유형 자체보다 더 안전합니다. 다음을 고려하세요:
type alias Email = String
type alias Name = String
someEmail = "[email protected]"
someName = "Benedict"
sendEmail : Email -> Cmd msg
sendEmail email = ...
위의 코드를 사용하면 sendEmail someName 수 있습니다. 실제로는 안된다하더라도 컴파일 할 것입니다. 왜냐하면 이름과 전자 메일이 모두 String 이긴하지만 완전히 다른 것들이기 때문입니다.
우리는 진정한 의미에서 새로운 유형 을 생성함으로써 한 String 을 유형 수준의 다른 String 과 구별 할 수 있습니다. 다음은 type alias 아닌 type Email 을 다시 작성하는 예제입니다.
module Email exposing (Email, create, send)
type Email = EmailAddress String
isValid : String -> Bool
isValid email =
-- ...validation logic
create : String -> Maybe Email
create email =
if isValid email then
Just (EmailAddress email)
else
Nothing
send : Email -> Cmd msg
send (EmailAddress email) = ...
우리의 isValid 함수는 문자열이 유효한 전자 메일 주소인지 여부를 결정합니다. create 함수는 지정된 String 이 올바른 전자 메일인지 확인하고 Maybe wrapped Email 을 반환하여 유효성이 검사 된 주소 만 반환하는지 확인합니다. EmailAddress "somestring" 을 작성하여 Email 직접 구성하여 유효성 검사를 회피 할 수 있지만 모듈 선언에서 EmailAddress 생성자가 노출되지 않으면 여기에 표시됩니다.
module Email exposing (Email, create, send)
다른 모듈은 EmailAddress 생성자에 액세스 할 수 없지만 여전히 주석의 Email 유형을 사용할 수 있습니다. 이 모듈 밖에서 새 Email 을 작성하는 유일한 방법은 제공하는 create 함수를 사용하는 것이며,이 함수는 유효한 이메일 주소 만 반환하도록합니다. 따라서이 API는 유형 안전성을 통해 사용자에게 올바른 경로를 자동으로 안내합니다. send 는 유효성 확인을 수행하는 create 에 의해 create 값과 만 작동하며 Maybe Email 반환하기 때문에 유효하지 않은 전자 메일의 처리를 강제합니다.
Email 생성자를 내보내려면 다음과 같이 작성할 수 있습니다.
module Email exposing (Email(EmailAddress), create, send)
이제 Email 을 가져 오는 파일도 생성자를 가져올 수 있습니다. 이 경우 사용자가 유효성 검사를 회피하고 유효하지 않은 이메일을 send 있지만 이와 같이 API를 작성하는 것은 아니므로 생성자를 내보내는 것이 유용 할 수 있습니다. 생성자가 여러 개인 유형의 경우 일부만 내보낼 수도 있습니다.
유형 생성
type alias 키워드 조합은 type alias 대해 새로운 이름을 제공하지만, 분리 된 type 키워드는 새로운 유형을 선언합니다. 의 이러한 종류의 가장 근본적인 중 하나를 살펴 보자 : Maybe
type Maybe a
= Just a
| Nothing
주의해야 할 첫번째 일은이다 Maybe 유형이 선언되는 형태 변수 의 . a 두 번째로 주목해야 할 것은 파이프 문자 인 | "또는"을 의미합니다. 즉, 유형의 무언가가 Maybe a 중 하나없는 Just a 또는 Nothing .
위의 코드를 작성할 때, Just 와 Nothing 은 값 생성자 로서 범위에 올 수 있습니다. Maybe 형식 생성자 로서 범위에 올 수 있습니다. 다음은 서명입니다.
Just : a -> Maybe a
Nothing : Maybe a
Maybe : a -> Maybe a -- this can only be used in type signatures
유형 변수 a 때문에 모든 유형을 Maybe 유형의 "wrapped inside"로 지정할 수 있습니다. Maybe Int , Maybe (List String) 또는 Maybe (Maybe (List Html)) 는 모두 유효한 유형입니다. case 표현식으로 모든 type 값을 구조 해제 할 case , 해당 유형의 가능한 각 인스턴스화를 고려해야합니다. Maybe a 유형의 값의 경우에는 Just a case와 Nothing case 모두를 고려해야합니다.
thing : Maybe Int
thing =
Just 3
blah : Int
blah =
case thing of
Just n ->
n
Nothing ->
42
-- blah = 3
case 표현식에 Nothing 절없이 위 코드를 작성하면 컴파일되지 않습니다. 이것은 Maybe 유형 생성자가 값이 Nothing 때의 논리를 처리하도록 강요하므로 존재하지 않을 수있는 값을 표현하는 좋은 패턴이되는 Nothing 입니다.
절대 입력하지 마십시오.
Never 타입을 생성 할 수 없습니다 ( Basics 모듈은 값 생성자를 내 보내지 않았고 Never 를 반환하는 다른 함수를 제공하지 않았습니다). never : Never 가치가 never : Never 또는 함수 createNever : ?? -> Never .
이것은 장점이 있습니다. 유형 시스템에서 일어날 수없는 가능성을 인코딩 할 수 있습니다. 이는 Task Never Int 와 같은 유형에서 볼 수 있습니다.이 유형은 Int 성공할 것이라는 것을 보장합니다. 또는 Program Never 는 자바 스크립트에서 Elm 코드를 초기화 할 때 매개 변수를 사용하지 않습니다.
특수 유형 변수
Elm은 컴파일러에게 특별한 의미를 갖는 다음과 같은 특수 유형 변수를 정의합니다.
comparable:Int,Float,Char,String및 이들의 튜플로 구성됩니다. 이렇게하면<및>연산자를 사용할 수 있습니다.예 : 목록 (
extent)에서 가장 작은 요소와 가장 큰 요소를 찾기위한 함수를 정의 할 수 있습니다. 어떤 유형의 서명을 쓸지 생각합니다. 한편으로extentInt : List Int -> Maybe (Int, Int)및extentChar : List Char -> Maybe (Char, Char)와Float및StringextentChar : List Char -> Maybe (Char, Char)있습니다. 이들의 구현은 동일합니다.extentInt list = let helper x (minimum, maximum) = ((min minimum x), (max maximum x)) in case list of [] -> Nothing x :: xs -> Just <| List.foldr helper (x, x) xsextent : List a -> Maybe (a, a)를 쓰려면 유추 할 수 있습니다extent : List a -> Maybe (a, a). 그러나 컴파일러는min과max함수가 이러한 타입에 대해 정의되지 않았기 때문에 이것을하지 않습니다.주의 : 이들은 단순한 래퍼입니다. 위에서 언급 한<연산자 주위).extent : List comparable -> Maybe (comparable, comparable)를 정의하여이를 해결할 수 있습니다extent : List comparable -> Maybe (comparable, comparable). 이렇게하면 솔루션이 다형성 이 될 수 있습니다. 이는 하나 이상의 유형에서 작동한다는 것을 의미합니다.number:Int및Float로 구성됩니다. Division을 제외한 산술 연산자를 사용할 수 있습니다. 예를 들어sum : List number -> number를 정의하여 정수 및 부동 소수점 모두에 적용 할 수 있습니다.appendable:String,List구성됩니다.++연산자를 사용할 수 있습니다.compappend: 이것은 때때로 나타나지만 컴파일러의 구현 세부 사항입니다. 현재이 프로그램은 사용자 자신의 프로그램에서 사용할 수 없지만 때때로 언급됩니다.
다음과 같은 형식 주석에서 : number -> number -> number 모두 동일한 형식을 참조하므로 Int -> Float -> Int 전달하면 형식 오류가 발생합니다. 형식 변수 이름에 접미사를 추가하여이 문제를 해결할 수 있습니다. number -> number' -> number'' 그러면 잘 컴파일됩니다.
이것들에 대한 정식 명칭은 없습니다.
- 특수 유형 변수
- Typeclass-like 타입 변수
- 의사 용 안경
이것은 Haskell의 Type Class 처럼 작동하지만 사용자가이를 정의 할 수있는 능력이 없기 때문입니다.