Elm Language
प्रकार, प्रकार चर, और प्रकार निर्माता
खोज…
टिप्पणियों
कृपया इन अवधारणाओं के साथ खुद को वास्तव में मास्टर करने के लिए खेलें! elm-repl ( elm-repl का परिचय देखें) संभवतः ऊपर दिए गए कोड के साथ खेलने के लिए एक अच्छी जगह है। आप elm-repl ऑनलाइन के साथ भी खेल सकते हैं।
तुलनीय डेटा प्रकार
तुलनात्मक प्रकार आदिम प्रकार हैं जिनकी तुलना मूल बातें मॉड्यूल से तुलना ऑपरेटरों का उपयोग करके की जा सकती है, जैसे: (<) , (>) , (<=) , (>=) , max , min , compare
एल्म में तुलनीय प्रकार Int , Float , Time , Char , String और ट्यूपल या तुलनीय प्रकार की सूची हैं।
प्रलेखन या प्रकार परिभाषाएं में वे एक विशेष प्रकार चर के रूप में भेजा जाता है comparable है, जैसे। प्रकार की Basics.max देखें।
max : comparable -> comparable -> comparable
हस्ताक्षर टाइप करें
एल्म में, मानों को एक नाम, एक बराबर चिह्न और फिर वास्तविक मूल्य लिखकर घोषित किया जाता है:
someValue = 42
तर्क के रूप में मूल्य या मूल्य लेने के अलावा कार्य भी मूल्य हैं। वे आम तौर पर इस प्रकार लिखे जाते हैं:
double n = n * 2
एल्म में प्रत्येक मूल्य का एक प्रकार है। उपरोक्त मानों का उपयोग कंपाइलर द्वारा अनुमान लगाया जाएगा कि वे कैसे उपयोग किए जाते हैं। लेकिन किसी भी शीर्ष-स्तरीय मूल्य के प्रकार को स्पष्ट रूप से घोषित करना सबसे अच्छा है, और ऐसा करने के लिए आप एक प्रकार लिखते हैं:
someValue : Int
someValue =
42
someOtherValue : Float
someOtherValue =
42
हम देख सकते हैं, 42 के रूप में या तो एक परिभाषित किया जा सकता Int या एक Float । यह सहज ज्ञान युक्त बनाता है, लेकिन अधिक जानकारी के लिए टाइप चर देखें।
फ़ंक्शन के साथ उपयोग किए जाने पर टाइप हस्ताक्षर विशेष रूप से मूल्यवान हैं। यहां पहले से दोगुना कार्य है:
double : Int -> Int
double n =
n * 2
इस बार, हस्ताक्षर में एक -> , एक तीर है, और हम हस्ताक्षर को "इंट टू इंट" के रूप में घोषित करेंगे, या "एक पूर्णांक लेता है और एक पूर्णांक देता है"। -> इंगित करता है कि देकर 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 किया जा सकता 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 । इसलिए, विभिन्न प्रकारों को लेने और वापस करने वाले कार्यों का उपयोग करने में सक्षम होने के लिए, आपको विभिन्न प्रकार के चर का उपयोग करना चाहिए।
उपनाम लिखें
कभी-कभी हम एक प्रकार को अधिक वर्णनात्मक नाम देना चाहते हैं। मान लीजिए कि हमारे ऐप में उपयोगकर्ताओं का प्रतिनिधित्व करने वाला डेटा प्रकार है:
{ 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 का उपयोग करना वास्तव में सिर्फ उपनाम देता है जैसा कि आप इसे देते हैं। ऊपर दिए गए 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
एक रिकॉर्ड प्रकार के लिए एक अन्य उपनाम घोषणा क्रम में प्रत्येक क्षेत्र के लिए एक तर्क के साथ एक निर्माण कार्य को परिभाषित करता है।
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 एक नए प्रकार बनाने के द्वारा टाइप स्तर पर। यहाँ एक उदाहरण दिया गया है कि Email को एक type बजाय एक type alias रूप में फिर से type alias :
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 सुनिश्चित करने के लिए कि हम केवल सत्यापित पते लौटने। हम एक का निर्माण करके सत्यापन जाँच से बचने सकते हैं Email लिख कर सीधे EmailAddress "somestring" , अगर हमारे मॉड्यूल घोषणा का खुलासा नहीं करता EmailAddress निर्माता शो के रूप में यहाँ,
module Email exposing (Email, create, send)
तब किसी अन्य मॉड्यूल के पास EmailAddress कंस्ट्रक्टर तक पहुंच नहीं होगी, हालांकि वे अभी भी एनोटेशन में Email प्रकार का उपयोग कर सकते हैं। एक नया निर्माण करने के लिए एक ही रास्ता Email इस मॉड्यूल के बाहर का उपयोग करना है create समारोह प्रदान करता है, और कहा कि समारोह सुनिश्चित है कि यह केवल पहली जगह में मान्य ईमेल पते वापस आ जाएगी। इसलिए, इस API स्वचालित रूप से अपने प्रकार सुरक्षा के माध्यम से सही रास्ते नीचे उपयोगकर्ता गाइड: send केवल द्वारा निर्मित मूल्यों के साथ काम करता है create है, जो एक मान्यता प्रदर्शन करती है, और अवैध ईमेल के निपटने के बाद से यह रिटर्न एक को लागू करता है Maybe Email ।
यदि आप Email निर्माता को निर्यात करना चाहते हैं, तो आप लिख सकते हैं
module Email exposing (Email(EmailAddress), create, send)
अब Email आयात करने वाली कोई भी फ़ाइल इसके निर्माता को भी आयात कर सकती है। इस मामले में, ऐसा करने से उपयोगकर्ता सत्यापन रद्द कर सकते हैं और अमान्य ईमेल send सकते हैं, लेकिन आप हमेशा इस तरह एक एपीआई का निर्माण नहीं कर रहे हैं, इसलिए निर्माण करने वालों को निर्यात करना उपयोगी हो सकता है। एक प्रकार के साथ जिसमें कई निर्माता हैं, आप केवल उनमें से कुछ को निर्यात करना चाहते हैं।
निर्माण प्रकार
type alias कीवर्ड संयोजन एक प्रकार के लिए एक नया नाम देता है, लेकिन type अलगाव में कीवर्ड एक नए प्रकार की घोषणा की। आइए इन प्रकारों में से एक सबसे मौलिक की जाँच करें: Maybe
type Maybe a
= Just a
| Nothing
ध्यान देने वाली पहली बात यह है कि Maybe प्रकार को एक प्रकार के चर के साथ घोषित किया जाता a । ध्यान देने योग्य दूसरी बात है पाइप पात्र, | , जो "या" का प्रतीक है। दूसरे शब्दों में, 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 । तो, Maybe Int , Maybe (List String) , या Maybe (Maybe (List Html)) , सभी मान्य प्रकार हैं। case एक्सप्रेशन के साथ किसी भी type मूल्य को नष्ट करते समय, आपको उस प्रकार के प्रत्येक संभावित इंस्टालेशन का हिसाब रखना चाहिए। मूल्य के प्रकार के मामले में Maybe a , आपको Just a केस, और Nothing केस दोनों के लिए Nothing :
thing : Maybe Int
thing =
Just 3
blah : Int
blah =
case thing of
Just n ->
n
Nothing ->
42
-- blah = 3
case अभिव्यक्ति में Nothing खंड के बिना उपरोक्त कोड लिखने का प्रयास करें: यह संकलन नहीं करेगा। यह वह है जो Maybe टाइप-कंस्ट्रक्टर को उन मूल्यों को व्यक्त करने के लिए एक महान पैटर्न बनाता है जो अस्तित्व में नहीं हो सकते हैं, क्योंकि यह आपको Nothing होने के तर्क को संभालने के लिए मजबूर करता है।
कभी नहीं प्रकार
Never प्रकार का निर्माण नहीं किया जा सकता है ( Basics मॉड्यूल ने अपने मूल्य निर्माणकर्ता को निर्यात नहीं किया है और आपको Never भी कोई अन्य फ़ंक्शन नहीं दिया है जो Never भी वापस Never लौटता है)। कोई मूल्य never : Never या एक समारोह createNever : ?? -> Never ।
इसके लाभ हैं: आप एक प्रकार की प्रणाली में एक संभावना को सांकेतिक शब्दों में बदलना कर सकते हैं जो ऐसा नहीं हो सकता है। यह Task Never Int जैसे प्रकारों में देखा जा सकता है जो गारंटी देता है कि यह एक Int साथ सफल होगा; या Program Never भी जो जावास्क्रिप्ट से एल्म कोड को इनिशियलाइज़ करते समय कोई पैरामीटर नहीं लेगा।
विशेष प्रकार की चर
एल्म निम्नलिखित विशेष प्रकार के चर को परिभाषित करता है, जो संकलक के लिए एक विशेष अर्थ रखते हैं:
comparable:Int,Float,Char,Stringऔर टुपल्स का संकलन। यह<और>ऑपरेटरों के उपयोग की अनुमति देता है।उदाहरण: आप एक सूची (
extent) में सबसे छोटे और सबसे बड़े तत्वों को खोजने के लिए एक फ़ंक्शन को परिभाषित कर सकते हैं। आप सोचते हैं कि किस प्रकार का हस्ताक्षर लिखना है। एक तरफ, आपextentInt : List Int -> Maybe (Int, Int)लिखextentInt : List Int -> Maybe (Int, Int)औरextentChar : List Char -> Maybe (Char, Char)और दूसराFloatऔरString। इनका कार्यान्वयन समान होगा: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) xsहो सकता है कि आपको केवल
extent : 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। विभाजन को छोड़कर अंकगणितीय संचालकों के उपयोग की अनुमति देता है। फिर आप उदाहरणsum : List number -> numberलिए परिभाषित कर सकते हैंsum : List number -> numberऔर यह दोनों किलों और तैरने के लिए काम करती है।appendable:String,Listसेappendable।++ऑपरेटर के उपयोग की अनुमति देता है।compappend: यह कभी-कभी दिखाई देता है, लेकिन कंपाइलर का कार्यान्वयन विवरण है। वर्तमान में यह आपके स्वयं के कार्यक्रमों में उपयोग नहीं किया जा सकता है, लेकिन कभी-कभी इसका उल्लेख किया जाता है।
ध्यान दें कि इस प्रकार एक एनोटेशन में: number -> number -> number ये सभी एक ही प्रकार को संदर्भित करते हैं, इसलिए Int -> Float -> Int एक प्रकार की त्रुटि होगी। आप इसे प्रकार चर नाम में एक प्रत्यय जोड़कर हल कर सकते हैं: number -> number' -> number'' तब ठीक संकलन करेगा।
इनका कोई आधिकारिक नाम नहीं है, इन्हें कभी-कभी कहा जाता है:
- विशेष प्रकार की चर
- टाइपकालेज़-जैसे टाइप वेरिएबल्स
- छद्म typeclasses
इसका कारण यह है कि वे हास्केल की टाइप क्लासेस की तरह काम करते हैं, लेकिन उपयोगकर्ता को इन को परिभाषित करने की क्षमता के बिना।