यह बहुत मुश्किल है, लेकिन एक अद्यतित ghc संस्करण (नवीनतम हैकेल) के साथ पूरी तरह से संभव है मंच काम करेगा)। इसके बारे में कई उदाहरण नहीं हैं (क्योंकि यह सब कुछ नया है), इसलिए मुझे उम्मीद है कि यह आपकी मदद करेगा।
आप सही हैं कि प्रकार के स्तर के प्राकृतिक उपयोग कर काम करेंगे। आपको दो मॉड्यूल की आवश्यकता होगी - एक संरचना को परिभाषित करने और एक सुरक्षित इंटरफ़ेस प्रदान करने के लिए, और वास्तविक सेवाओं को परिभाषित करने के लिए दूसरा।
{-# LANGUAGE TypeOperators, PolyKinds, RankNTypes #-}
{-# LANGUAGE KindSignatures, DataKinds, TypeFamilies, UndecidableInstances #-}
module DefinedServices where
import ServiceTypes
import Control.Monad
apacheInstalled :: Service '[443] ServiceDetails
apacheInstalled = makeService "apache" $ putStrLn "Apache service"
torBridge :: Service [80,443] ServiceDetails
torBridge = makeService "tor" $ putStrLn "Tor service"
httpService :: Service [80, 8080] ServiceDetails
httpService = makeService "http" $ putStrLn "Http service"
serviceList1 :: [ServiceDetails]
serviceList1 = getServices $
noServices `addService` httpService `addService` apacheInstalled
-- serviceList2 :: [ServiceDetails]
-- serviceList2 = getServices $
-- noServices `addService` apacheInstalled `addService` torBridge
main = startServices serviceList1
नोट कैसे प्रत्येक सेवा के लिए बंदरगाहों प्रकार से परिभाषित किया जाता:
इस प्रयोग में कोड है। serviceList1
httpService
और apacheInstalled
सेवा का उपयोग करता है। यह संकलित करता है, क्योंकि उनके बंदरगाह संघर्ष नहीं करते हैं। serviceList2
बाहर टिप्पणी की है, और इस संकलन त्रुटि अगर uncommented का कारण बनता है:
DefinedServices.hs:22:56:
Couldn't match type 'False with 'True
Expected type: 'True
Actual type: ServiceTypes.UniquePorts '[443, 80, 443]
In the second argument of `($)', namely
`noServices `addService` apacheInstalled `addService` torBridge'
In the expression:
getServices
$ noServices `addService` apacheInstalled `addService` torBridge
In an equation for `serviceList2':
serviceList2
= getServices
$ noServices `addService` apacheInstalled `addService` torBridge
Failed, modules loaded: ServiceTypes.
यह बहुत अच्छी तरह से समस्या का वर्णन करता: UniquePorts के रूप में 443 दो बार प्रयोग किया जाता है झूठी किया जा रहा समाप्त होता है।
तो यहाँ है कि यह कैसे ServiceTypes.hs
में किया जाता है:
{-# LANGUAGE TypeOperators, PolyKinds, RankNTypes #-}
{-# LANGUAGE KindSignatures, DataKinds, TypeFamilies, UndecidableInstances #-}
module ServiceTypes (
makeService, noServices, addService, Service, ServiceDetails(..)
, getServices, startServices) where
import GHC.TypeLits
import Control.Monad
import Data.Type.Equality
import Data.Type.Bool
हम भाषा एक्सटेंशन की एक पूरी गुच्छा की जरूरत है इस काम करने के लिए मिलता है। इसके अलावा, सुरक्षित इंटरफ़ेस परिभाषित किया गया है।
सबसे पहले, यह जांचने के लिए कि एक सूची अद्वितीय है या नहीं, एक प्रकार के स्तर की फ़ंक्शन की आवश्यकता है। यह Data.Type.Equality
और Data.Type.Bool
में परिवार ऑपरेटरों के प्रकार का लाभ उठाता है। ध्यान दें कि निम्न कोड केवल टाइपशेकर द्वारा निष्पादित किया जाता है।
type family UniquePorts (list1 :: [Nat]) :: Bool
type instance UniquePorts '[] = True
type instance UniquePorts (a ': '[]) = True
type instance UniquePorts (a ': b ': rest) = Not (a == b) && UniquePorts (a ': rest) && UniquePorts (b ': rest)
यह अद्वितीय की एक पुनरावर्ती परिभाषा है।
इसके बाद, के रूप में हम एक साथ कई सेवाओं का उपयोग करेगा, वहाँ एक में दो सूचियों संयोजित करने का उपाय करने की आवश्यकता होगी:
type family Concat (list1 :: [a]) (list2 :: [a]) :: [a]
type instance Concat '[] list2 = list2
type instance Concat (a ': rest) list2 = a ': Concat rest list2
सभी प्रकार के स्तर के कार्यों हम यह अपेक्षा है कि! एक भी सेवा की वास्तविक जानकारी के लिए,
data Service (ports :: [Nat]) service = Service service
अगला:
इसके बाद, मैं एक Service
प्रकार, कि जरूरत पोर्ट के साथ एक और प्रकार लपेटता परिभाषित करेगा।अब अंत में कई सेवाओं सूचियों के लिए
makeService :: String -> IO() -> Service ports ServiceDetails
makeService name action = Service $ ServiceDetails name action
: आप आप क्या जरूरत को यह अनुकूलित करना चाहिए:
data ServiceDetails = ServiceDetails {
serviceName :: String
, runService :: IO()
}
मैं भी परिभाषित बंदरगाहों के साथ एक Service
प्रकार में एक सेवा रैप करने के लिए एक सहायक समारोह गयी। जहां यह सब एक साथ आता है
noServices :: Service '[] [ServiceDetails]
noServices = Service []
addService
है:
addService :: (finalPorts ~ Concat ports newPorts, UniquePorts finalPorts ~ True)
=> Service ports [ServiceDetails]
-> Service newPorts ServiceDetails
-> Service finalPorts [ServiceDetails]
addService (Service serviceList) (Service newService) =
Service $ (newService : serviceList)
finalPorts ~ Concat ports newPorts
सूची में केवल finalPorts
बंदरगाहों के संयोजन बनाता है `noServices बस सेवाओं की एक खाली सूची है, जो स्पष्ट रूप से कोई बंदरगाहों का उपयोग करता परिभाषित करता है सेवाओं और नई सेवा का। UniquePorts finalPorts ~ True
सुनिश्चित करता है कि अंतिम बंदरगाहों में कोई डुप्लिकेट बंदरगाह नहीं है। बाकी का कार्य पूरी तरह से तुच्छ है।
getServices
से [ServiceDetails]
को अनचाहे करता है। Service
कन्स्ट्रक्टर को सार्वजनिक नहीं किया गया है, Service ports [ServiceDetails]
प्रकार बनाने का एकमात्र तरीका noServices
और addService
फ़ंक्शंस के माध्यम से सुरक्षित है, जो सुरक्षित होने की गारंटी है।
getServices :: Service ports [ServiceDetails] -> [ServiceDetails]
getServices (Service details) = details
अंत में सेवाएं चलाने के लिए एक परीक्षण के समारोह: इस नई कार्यक्षमता के लिए
startServices :: [ServiceDetails] -> IO()
startServices services = forM_ services $ \service -> do
putStrLn $ "Starting service " ++ (serviceName service)
runService service
putStrLn "------"
संभावनाएं लगभग अंतहीन हैं, और पिछले GHC संस्करणों के ऊपर एक विशाल सुधार (यह अभी भी संभव था , लेकिन अधिक अधिक कठिन)। एक बार जब आप प्रकारों में मूल्यों का उपयोग करके अपना सिर प्राप्त कर लेते हैं तो यह कोड बहुत आसान होता है।
यह दर्दनाक होने वाला है। मैं अनुशंसा करता हूं [स्मार्ट कन्स्ट्रक्टर] (http://www.haskell.org/haskellwiki/Smart_constructors) – luqui