هل هناك طريقة لإلغاء تغليف نوع من IO monad؟
سؤال
لدي هذه الوظيفة البسيطة جدًا
import qualified Data.ByteString.Lazy as B
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
readJFile :: IO (Maybe Response)
readJFile = parsing >>= (\d ->
case d of
Left err -> return Nothing
Right ps -> return (Just ps))
where parsing = fmap eitherDecode getJson :: IO (Either String Response)
أين jsonFile
هو مسار إلى ملف موجود على محرك الأقراص الثابتة الخاص بي (عذرًا على عدم وجود تدوين، لكنني وجدت هذا أكثر وضوحًا للعمل معه)
سؤالي هو؛هل هناك طريقة بالنسبة لي للتخلي عن IO
جزء حتى أتمكن من العمل مع bytesting وحدها؟
أعلم أنه يمكنك مطابقة النمط مع بعض الأحاديات مثل Either
و Maybe
للحصول على قيمهم، ولكن هل يمكنك أن تفعل شيئا مماثلا مع IO
?
أو بصوت مختلف:هل هناك طريقة بالنسبة لي لجعل readJFile
يعود Maybe Response
بدون IO؟
المحلول
للتوسع في تعليقاتي، إليك كيف يمكنك القيام بذلك:
giveacodicetagpre.في النهاية، يمكنك الجمع بين كل شيء في إجراء IO مرة أخرى:
giveacodicetagpre.نصائح أخرى
لن تحتاج أبدًا إلى "سحب موناد" عبر أي وظيفة، إلا إذا كانت جميعها تحتاج إلى إجراء عملية IO فعليًا.فقط ارفع السلسلة بأكملها إلى الموناد fmap
(أو liftM
/ liftM2
/ ...).
على سبيل المثال،
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
ومن المفترض أن يكون الأمر برمته هكذا
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
يمكنك القيام بذلك ببساطة
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
بالمناسبة، قد يبدو هذا أجمل مع do
الرموز:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
فيما يتعلق بك تحرير والسؤال
هل هناك طريقة بالنسبة لي لجعل
readJFile
يعودMaybe Response
بدون الIO
?
لا, ، هذا لا يمكن أن ينجح، لأنه readJFile
لا حاجة للقيام IO.لا توجد وسيلة للهروب من IO
موناد إذن، هذا هو بيت القصيد من ذلك!(حسنا، هناك unsafePerformIO
كما يقول ريكاردو، ولكن هذا بالتأكيد ليس تطبيقًا صالحًا لذلك.)
إذا كان الأمر يتعلق بصعوبة التفريغ Maybe
القيم في IO
الموناد، والتوقيعات التي تحتوي على أقواس، قد ترغب في إلقاء نظرة على MaybeT
محول.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
بشكل عام، نعم، هناك طريقة.يرافقه الكثير من "لكن"، ولكن هناك.أنت تسأل عما يسمى عملية إدخال/إخراج غير آمنة: System.IO.Unsafe.يتم استخدامه لكتابة الأغلفة عند الاتصال بالمكتبات الخارجية عادةً، وهو ليس شيئًا يمكن اللجوء إليه في كود هاسكل العادي.
في الأساس، يمكنك الاتصال unsafePerformIO :: IO a -> a
الذي يفعل بالضبط ما تريد، فإنه يزيل IO
جزء ويعطيك قيمة ملفوفة مرة أخرى من النوع a
.لكن، إذا نظرت إلى الوثائق، هناك عدد من المتطلبات التي يجب أن تضمنها لنفسك أمام النظام، والتي تنتهي جميعها في نفس الفكرة:على الرغم من أنك أجريت العملية عبر الإدخال/الإخراج، إلا أن الإجابة يجب أن تكون نتيجة دالة، كما هو متوقع من أي دالة هاسكل أخرى لا تعمل في IO
:يجب أن يكون لها دائمًا نفس النتيجة دون آثار جانبية، بناءً على قيم الإدخال فقط.
هنا، نظرا للكود الخاص بك، قطعا هذه ليست القضيه, ، لأنك تقرأ من ملف.يجب عليك فقط مواصلة العمل داخل IO monad، عن طريق الاتصال بـ IO monad readJFile
من داخل وظيفة أخرى مع نوع النتيجة IO something
.وبعد ذلك، ستتمكن من قراءة القيمة داخل ملف IO
غلاف (يتواجد في IO
نفسك)، اعمل عليها، ثم أعد لف النتيجة في أخرى IO
عند العودة.
No, there is no safe way to get a value out of the IO monad. Instead you should do the work inside the IO monad by applying functions with fmap or bind (>>=). Also you should use decode instead of eitherDecode when you want your result to be in Maybe.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
You could also use do notation if that is clearer to you:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Note that you dont even need the parseResponse function since readJFile specifies the type.