惯用的 monadic 版本的 maximumBy 会是什么样子?
What would an idiomatic, monadic version of maximumBy look like?
如何获得有效容器的最大元素,其中要比较的计算属性也会触发效果?
必须有更易读的方式来做这样的事情:
latest dir = Turtle.fold (z (ls dir)) Fold.maximum
z :: MonadIO m => m Turtle.FilePath -> m (UTCTime, Turtle.FilePath)
z mx = do
x <- mx
d <- datefile x
return (d, x)
我使用了重载版本而不是非重载版本 maximumBy
但后者似乎更适合临时属性选择。
如何更有条理地解决类似问题?
所以我对Turtle一无所知;不知道这是否适合 Turtle 生态系统的其余部分。但是既然你在评论中说服了我 maximumByM
值得手写,我会这样做:
maximumOnM :: (Monad m, Ord b) => (a -> m b) -> [a] -> m a
maximumOnM cmp [x] = return x -- skip the effects if there's no need for comparison
maximumOnM cmp (x:xs) = cmp x >>= \b -> go x b xs where
go x b [] = return x
go x b (x':xs) = do
b' <- cmp x'
if b < b' then go x' b' xs else go x b xs
我通常更喜欢事物的 *On 版本——它采用映射到 Ord
erable 元素的函数——而不是 *By 版本——它采用直接进行比较的函数。 maximumByM
类似,但类型类似于 Monad m => (a -> a -> m Ordering) -> [a] -> m a
,但这可能会迫使您为每个 a
重做效果,我猜这不是您想要的。我发现 *On 更符合我想做的事情和我想要的性能特征。
使用 reducers 包可以 运行 对可折叠产品产生影响。
我不确定它是否正确,但它利用了现有的组合器和实例(Bounded (Maybe a)
除外)。
import Data.Semigroup.Applicative (Ap(..))
import Data.Semigroup.Reducer (foldReduce)
import Data.Semigroup (Max(..))
import System.IO (withFile, hFileSize, IOMode(..))
-- | maxLength
--
-- >>> getMax $ maxLength ["abc","a","hello",""]
-- 5
maxLength :: [String] -> (Max Int)
maxLength = foldReduce . map (length)
-- | maxLengthIO
--
-- Note, this runs IO...
--
-- >>> (getAp $ maxLengthIO ["package.yaml", "src/Lib.hs"]) >>= return . getMax
-- Just 1212
--
-- >>> (getAp $ maxLengthIO []) >>= return . getMax
-- Nothing
maxLengthIO :: [String] -> Ap IO (Max (Maybe Integer))
maxLengthIO xs = foldReduce (map (fmap Just . f) xs) where
f :: String -> IO Integer
f s = withFile s ReadMode hFileSize
instance Ord a => Bounded (Maybe a) where
maxBound = Nothing
minBound = Nothing
既然你已经熟悉了Fold
,你可能想了解一下FoldM
,这是相似的。
data FoldM m a b =
-- FoldM step initial extract
forall x . FoldM (x -> a -> m x) (m x) (x -> m b)
你可以写:
maximumOnM ::
(Ord b, Monad m)
=> (a -> m b) -> FoldM m a (Maybe a)
maximumOnM f = FoldM combine (pure Nothing) (fmap snd)
where
combine Nothing a = do
f_a <- f a
pure (Just (f_a, a))
combine o@(Just (f_old, old)) new = do
f_new <- f new
if f_new > f_old
then pure $ Just (f_new, new)
else pure o
现在您可以使用 Foldl.foldM
来 运行 列表(或其他 Foldable
容器)的折叠。与 Fold
一样,FoldM
有一个 Applicative
实例,因此您可以将多个有效折叠组合成一个,交错每个折叠的效果并组合它们的结果。
如何获得有效容器的最大元素,其中要比较的计算属性也会触发效果?
必须有更易读的方式来做这样的事情:
latest dir = Turtle.fold (z (ls dir)) Fold.maximum
z :: MonadIO m => m Turtle.FilePath -> m (UTCTime, Turtle.FilePath)
z mx = do
x <- mx
d <- datefile x
return (d, x)
我使用了重载版本而不是非重载版本 maximumBy
但后者似乎更适合临时属性选择。
如何更有条理地解决类似问题?
所以我对Turtle一无所知;不知道这是否适合 Turtle 生态系统的其余部分。但是既然你在评论中说服了我 maximumByM
值得手写,我会这样做:
maximumOnM :: (Monad m, Ord b) => (a -> m b) -> [a] -> m a
maximumOnM cmp [x] = return x -- skip the effects if there's no need for comparison
maximumOnM cmp (x:xs) = cmp x >>= \b -> go x b xs where
go x b [] = return x
go x b (x':xs) = do
b' <- cmp x'
if b < b' then go x' b' xs else go x b xs
我通常更喜欢事物的 *On 版本——它采用映射到 Ord
erable 元素的函数——而不是 *By 版本——它采用直接进行比较的函数。 maximumByM
类似,但类型类似于 Monad m => (a -> a -> m Ordering) -> [a] -> m a
,但这可能会迫使您为每个 a
重做效果,我猜这不是您想要的。我发现 *On 更符合我想做的事情和我想要的性能特征。
使用 reducers 包可以 运行 对可折叠产品产生影响。
我不确定它是否正确,但它利用了现有的组合器和实例(Bounded (Maybe a)
除外)。
import Data.Semigroup.Applicative (Ap(..))
import Data.Semigroup.Reducer (foldReduce)
import Data.Semigroup (Max(..))
import System.IO (withFile, hFileSize, IOMode(..))
-- | maxLength
--
-- >>> getMax $ maxLength ["abc","a","hello",""]
-- 5
maxLength :: [String] -> (Max Int)
maxLength = foldReduce . map (length)
-- | maxLengthIO
--
-- Note, this runs IO...
--
-- >>> (getAp $ maxLengthIO ["package.yaml", "src/Lib.hs"]) >>= return . getMax
-- Just 1212
--
-- >>> (getAp $ maxLengthIO []) >>= return . getMax
-- Nothing
maxLengthIO :: [String] -> Ap IO (Max (Maybe Integer))
maxLengthIO xs = foldReduce (map (fmap Just . f) xs) where
f :: String -> IO Integer
f s = withFile s ReadMode hFileSize
instance Ord a => Bounded (Maybe a) where
maxBound = Nothing
minBound = Nothing
既然你已经熟悉了Fold
,你可能想了解一下FoldM
,这是相似的。
data FoldM m a b =
-- FoldM step initial extract
forall x . FoldM (x -> a -> m x) (m x) (x -> m b)
你可以写:
maximumOnM ::
(Ord b, Monad m)
=> (a -> m b) -> FoldM m a (Maybe a)
maximumOnM f = FoldM combine (pure Nothing) (fmap snd)
where
combine Nothing a = do
f_a <- f a
pure (Just (f_a, a))
combine o@(Just (f_old, old)) new = do
f_new <- f new
if f_new > f_old
then pure $ Just (f_new, new)
else pure o
现在您可以使用 Foldl.foldM
来 运行 列表(或其他 Foldable
容器)的折叠。与 Fold
一样,FoldM
有一个 Applicative
实例,因此您可以将多个有效折叠组合成一个,交错每个折叠的效果并组合它们的结果。