Haskell Turtle выходит из Shell Monad

Не могли бы вы помочь мне с библиотекой черепахи. Я хочу написать простую программу, которая рассчитывает использование дискового пространства. Вот код:

getFileSize :: FilePath -> IO Size
getFileSize f = do
  status <- stat f
  return $ fileSize status

main = sh $ do
  let sizes = fmap getFileSize $ find (suffix ".hs") "."

так что теперь у меня есть sizes тип привязки Shell (IO Size), Но я не могу просто подвести итог, с sum сложить, потому что есть IO Size там. Если бы это было что-то вроде [IO Size] Я мог вытащить IO монада оттуда с помощью sequence превратить его в IO [Size], Но я не могу сделать это с Shell монада, так как это не Traversable, Поэтому я написал что-то вроде этого

import qualified Control.Foldl as F

main = sh $ do
  let sizes = fmap getFileSize $ find (suffix ".hs") "."
  lst <- fold sizes F.list
  let cont = sequence lst
  sz <- liftIO $ cont
  liftIO $ putStrLn (show (sum sz))  

Сначала я сложил Shell (IO Size) в [IO Size] а затем IO [Size] подвести итоги списка потом. Но мне интересно, есть ли более каноническое или элегантное решение для этого, потому что здесь я создал два списка для выполнения моей задачи. И я об этом подумал Shell монада для манипулирования сущностями в постоянном пространстве. Может быть, есть некоторые fold делать IO (Shell Size) от Shell (IO Size)?

Благодарю.

2 ответа

Решение

У вас есть IO действие, и вы действительно хотите Shell действие. Обычный способ справиться с этим с liftIO метод, который доступен потому, что Shell это пример MonadIO,

file <- find (suffix ".hs") "."
size <- liftIO $ getFileSize file

или даже

size <- liftIO . getFileSize =<< find (suffix ".hs") "."

К счастью, Turtle Сам пакет предлагает некоторые функции размера, которые вы можете использовать непосредственно с MonadIO примеры как Shell вTurtle.Prelude так что вам не нужно использовать liftIO сам.

Теперь вы должны суммировать их, но вы можете сделать это с fold а также sum,

Я бы порекомендовал вам не взламывать Shell набери сам. Это должно быть зарезервировано для добавления совершенно новой функциональности в API. Это, конечно, не нужно в этом случае.

На самом деле мне удалось избавиться от IO здесь с помощью вспомогательного преобразования

sio :: Shell (IO a) -> Shell a
sio s = Shell (\(FoldShell step begin done) ->
                let step' x a = do
                      a' <- a
                      step x a'
                in
                  _foldShell s (FoldShell step' begin done))

Но теперь мне интересно, есть ли более простое решение этой задачи...

Другие вопросы по тегам