Haskell - получение типа Char при ожидании [Char] в List Monad - PullRequest
0 голосов
/ 02 мая 2019

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

builddir xs = do
    writeto  <- lastest getArgs
    let folderl b = searchable <$> (getPermissions b)
    let filel   c = ((lastlookup mlookup c) &&) <$> ((not <$> folderl c))
    a <- listDirectory xs
    listdirs <- filterM (folderl) (map ((xs ++ "/") ++) a)
    filedirs <- filterM (filel)   (map ((xs ++ "/") ++) a)
    let tagfiles = mapM (tagsort) filedirs
    putStrLn $ concat listdirs
    putStrLn $ concat tagfiles


tagsort xs = do
    nsartist <- getTags xs artistGetter
    nsalbum  <- getTags xs albumGetter
    artist   <- init $ drop 8 $ show nsalbum
    album    <- init $ drop 7 $ show nsalbum
    (artist ++ " - " ++ album)

Я знаю, это очень грязно.Когда я запускаю ghci, я получаю следующее:

• Couldn't match expected type ‘[Char]’ with actual type ‘Char’
• In the first argument of ‘(++)’, namely ‘artist’
  In a stmt of a 'do' block: artist ++ " - " ++ album
  In the expression:
    do nsartist <- getTags xs artistGetter
       nsalbum <- getTags xs albumGetter
       artist <- init $ drop 8 $ show nsalbum
       album <- init $ drop 7 $ show nsalbum
       ....
       60    artist ++ " - " ++ album

Мне трудно понять, почему это происходит.Выполнение аналогичной команды в моей тестовой программе:

main = do
artg <- getTags "/home/spilskinanke/backlogtest/02 - await rescue.mp3" artistGetter
let test = init $ drop 8 $ show artg
print test

, это работает точно.Выводит строку «65daysofstatic» на мой терминал в ghci.Это явно не тип Char.Так почему в моем коде его называют Чаром?Также обратите внимание, что перед добавлением любых фрагментов кода, которые ссылаются на модуль метаданных, который я использую (htaglib), эта программа работала нормально в тесте.При отсутствии функции tagfiles и монады tagort я смог установить arg для определенного каталога, и мой тест успешно распечатал список FilePaths, содержащий все читаемые папки, и другой список FilePaths, содержащий все файлы, заканчивающиеся тем, что я хотел вmlookup, в данном случае это .mp3, .mp4, .flac и .wav.Любая помощь будет оценена.

1 Ответ

6 голосов
/ 02 мая 2019

Вы смешиваете IO и [] в tagsort:

tagsort xs = do

    -- Okay, run IO action and bind result to ‘nsartist’
    nsartist <- getTags xs artistGetter

    -- Similarly for ‘nsalbum’
    nsalbum  <- getTags xs albumGetter

    -- Mistaken: ‘init …’ returns a list, not an ‘IO’ action
    artist   <- init $ drop 8 $ show nsalbum
    album    <- init $ drop 7 $ show nsalbum

    -- You are also missing a ‘pure’ or ‘return’ here
    (artist ++ " - " ++ album)

Исправления просты: используйте оператор let вместо оператора связывания <-, идобавьте pure, чтобы сделать IO String из String, который у вас есть:

tagsort xs = do
    nsartist <- getTags xs artistGetter
    nsalbum  <- getTags xs albumGetter
    let artist = init $ drop 8 $ show nsalbum
    let album = init $ drop 7 $ show nsalbum
    pure (artist ++ " - " ++ album)

Вообще говоря, каждый блок do должен находиться в одной монаде, пока вы не начнете изучать использованиемонадные трансформаторы для комбинирования разных эффектов.Таким образом, в блоке IO все, что находится справа от оператора связывания, должно быть действием IO;если вы просто хотите делать чистые вычисления, вы можете использовать let (или просто встроенные выражения, если вам не нужно привязывать что-либо к имени).Наконец, последнее выражение в блоке do также должно быть действием в конкретной монаде - это часто чистое значение, просто заключенное в монаду с помощью pure :: Applicative f => a -> f a (или return :: Monad m => a -> m a, что делает то же самое, ноработает в несколько меньшем количестве контекстов из-за более ограничительного ограничения Monad.

...