سؤال

لنفترض أنه في برنامج هاسكل لدي بعض البيانات التي يكون نوعها مثل:

  • IO [ IO (Int, String, Int) ], ، أو
  • IO [ (Int, String, IO Int) ], ، أو
  • [ (Int, String, IO Int) ]

ولكن لدي وظائف نقية يجب أن تعمل عليها [ (Int, String, Int) ].يبدو أنني سأضطر إلى إزالة القيم الداخلية من IO monad بطريقة خرقاء، حتى أحصل على شيء مثل IO [(Int, string, Int)] ثم (من داخل IO monad) قم بتطبيق الوظائف النقية.لا توجد طريقة سهلة محددة مسبقًا للقيام بذلك، على ما أعتقد؟شيء من شأنه أن يرفع بنية البيانات بأكملها إلى موناد، ويحول كل شيء داخل الأنواع إلى أنواع نقية؟(سيكون ذلك مريحًا جدًا!)

هل كانت مفيدة؟

المحلول

يمكنك استخدام liftM* وظيفة من Control.Monad الوحدة النمطية، أو liftA* وظائف ل تطبيقات.

liftM يسمح لك برفع وظيفة خالصة للعمل داخل Monad، على سبيل المثال:

ghci> let s = return "Hello" :: IO String
ghci> liftM reverse s
"olleH"

بهذه الطريقة لن تضطر إلى كتابة أشياء مثل "s >>= \x -> return (reverse x)" في كل مكان.

على الرغم من أن هذا لن يساعدك في عملك [(String, Int, IO Int)] على سبيل المثال، إذا كانت الوظيفة الخالصة لديك تتعامل مع ملف [(String, Int, Int)].نظرًا لأن العنصر الثالث في المجموعة ليس في الواقع عنصرًا Int.

في هذه الحالة أقترح أن أكتب دالة أولاً [(String, Int, IO Int)] -> IO [(String, Int, Int)] والتي تطبق الدالة النقية المرفوعة.


هذه هي الوظيفة الأكثر عمومية التي يمكنني التوصل إليها للقيام بذلك:

conv :: Monad m => (f (m a) -> m (f a)) -> [f (m a)] -> m [f a]
conv f = sequence . map f

يمكنك تسميتها هكذا:

liftTrd :: Monad m => (a, b, m c) -> m (a, b, c)
liftTrd (x, y, mz) = mz >>= \z -> return (x, y, z)

conv liftTrd [("hi", 4, return 2)] :: IO [(String, Int, Int)]

لن تعمل هذه الوظيفة إلا إذا كان لديك موناد واحد موجود في مكان ما عميقًا في النوع.إذا كان لديك عدة أشخاص، أعتقد أنه يجب عليك التفكير حقًا في النوع الذي تعمل به ومعرفة ما إذا كان لا يمكنك جعل الأمر أسهل.

نصائح أخرى

أولاً، تم استدعاء بعض أمثلة الاستخدام للحل أدناه reduce (ما لم تقترح اسمًا أفضل):

> reduce [(["ab", "c"], "12")] :: [(String, String)]
[("ab","12"),("c","12")]

> reduce [(["ab", "c"], "12")] :: [(Char, Char)]
[('a','1'),('a','2'),('b','1'),('b','2'),('c','1'),('c','2')]

> reduce [("ab", "12"), ("cd", "3")] :: [(Char, Char)]
[('a','1'),('a','2'),('b','1'),('b','2'),('c','3'),('d','3')]

تم حل المثال الخاص بك أيضًا به:

complexReduce :: Monad m => m (m (a, b, m [m (c, m d)])) -> m (a, b, [(c, d)])
complexReduce = reduce

وتنفيذ reduce:

{-# LANGUAGE FlexibleContexts, FlexibleInstances, IncoherentInstances, MultiParamTypeClasses, UndecidableInstances #-}

import Control.Monad

-- reduce reduces types to simpler types,
-- when the reduction is in one of the following forms:
-- * make a Monad disappear, like join
-- * move a Monad out, like sequence
-- the whole magic of Reduce is all in its instances
class Reduce s d where
  reduce :: s -> d

-- Box is used only for DRY in Reduce instance definitions.
-- Without it we, a Reduce instance would need
-- to be tripled for each variable:
-- Once for a pure value, once for a monadic value,
-- and once for a reducable value
newtype Box a = Box { runBox :: a }
instance Monad m => Reduce (Box a) (m a) where
  reduce = return . runBox
instance Reduce a b => Reduce (Box a) b where
  reduce = reduce . runBox
redBox :: Reduce (Box a) b => a -> b
redBox = reduce . Box

-- we can join
instance (Monad m
  , Reduce (Box a) (m b)
  ) => Reduce (m a) (m b) where
  reduce = join . liftM redBox

-- we can sequence
-- * instance isnt "Reduce [a] (m [b])" so type is always reduced,
--   and thus we avoid overlapping instances.
-- * we cant make it general for any Traversable because then
--   the type system wont find the right patterns.
instance (Monad m
  , Reduce (Box a) (m b)
  ) => Reduce (m [a]) (m [b]) where
  reduce = join . liftM (sequence . fmap redBox)

instance (Monad m
  , Reduce (Box a) (m c)
  , Reduce (Box b) (m d)
  ) => Reduce (a, b) (m (c, d)) where
  reduce (a, b) = liftM2 (,) (redBox a) (redBox b)

instance (Monad m
  , Reduce (Box a) (m d)
  , Reduce (Box b) (m e)
  , Reduce (Box c) (m f)
  ) => Reduce (a, b, c) (m (d, e, f)) where
  reduce (a, b, c) =
    liftM3 (,,) (redBox a) (redBox b) (redBox c)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top