【问题标题】:Haskell: generate HaTeX output from IO [[String]]Haskell:从 IO [[String]] 生成 HaTeX 输出
【发布时间】:2014-08-13 21:12:41
【问题描述】:

我正在尝试编写一个从基于文本的输入文件生成 Latex 代码的 Haskell 程序。为此,我想使用HaTeX

作为第一步,我想为存储在文本文件中的电影生成指向 IMDB 的链接。格式为每行一部电影,标题和 IMDB URL 由 | 分隔。人物:

Movie 1 | http://url1
Movie 2 | http://url2

我目前的代码如下:

import System.IO
import Data.Char(toUpper)
import Data.List.Split(splitOn)

import Text.LaTeX.Packages.Hyperref
import Text.LaTeX

main :: IO()
main = execLaTeXT generateFromFile  >>= renderFile "output.txt"

generateFromFile :: Monad m => LaTeXT_ m
generateFromFile = do
  m <- getMovies -- AFAIK this should "bind" the IO Monad, i.e. m should be [[String]]
  (generateFromStrLists m)

-- Takes a list of lists of strings and generates links
generateFromStrLists :: Monad m => [[String]] -> LaTeXT_ m
generateFromStrLists [] = fromString ""
generateFromStrLists (y:x) = do
  (generate u t)
  (generateFromStrLists x)
  where
    u = y !! 0
    t = y !! 1

-- Generate the movie link for a given title and URL
generate :: Monad m => String -> String -> LaTeXT_ m
generate u t = (href [] (createURL u) (fromString t))

getMovies :: IO [[String]]
getMovies = do
  lines <- getLines "movies.lst" -- [String]
  return $ map (splitOn "|") lines

getLines :: FilePath -> IO [String]
getLines filename = do
  text <- readFile filename
  return $ lines text

如果我使用手工制作的 [[String]] 手动调用 generateFromStrLists,则输出会正确生成。

我猜我看到的问题与 Monads 有关(但我不确定)。我猜我 1) 要么不充分理解 IO Monad,要么 2) 混淆了使用 IO Monad 和 Monad 来正确生成 Latex 代码(可能在 generateFromStrLists 中)。

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    您需要将lift IO 操作转换为LaTeXT_ IO 操作:

    generateFromFile :: LaTeXT_ IO
    generateFromFile = do
        m <- liftIO getMovies
        generateFromStrLists m
    

    您还必须从类型签名中删除 Monad m 约束,并指定它正在处理 IO Monad。


    简短的解释是 LaTeXT_ 是一个 Monad 转换器,它本质上可以将 LaTeX monad 的行为添加到任何其他 Monad。你特别想在LaTeXT_ m Monad 中执行IO,所以m 必须是IO(或更一般地说MonadIO m =&gt; LaTeXT_ m 可以工作)。这为您提供了将多种类型的转换器“堆叠”在一起的能力,通常在此堆栈的底部有IO(没有IO 转换器,它太原始了),这样您就可以对于复杂的应用程序有各种不同的行为。

    所以 liftIO 所做的是将操作从 IO 像 monad(在本例中特别是 IO monad)“提升”到您正在使用的转换器 monad 中。对于一般的转换器,有lift 操作,但由于执行IO 是一个非常常见的操作,所以有一个专门的liftIO 操作具有签名

    liftIO :: MonadIO m => IO a -> m a
    

    LaTeXT IO monad 的上下文中,它的类型将被专门化为

    liftIO :: IO a -> LaTeXT IO a
    

    所以liftIO getMovies 将具有LaTeXT IO [[String]] 类型。在特定的 monad 计算中,所有表达式必须属于同一个 monad,否则可能会出现以下情况:

    import Data.Map as M
    
    getValues :: String -> String -> Map String Int -> Maybe (Int, Int)
    getValues s1 s2 m = do
        v1 <- M.lookup s1 m
        v2 <- M.lookup s2 m
        print (v1, v2)
        return (v1, v2)
    

    由于M.lookupMaybe monad 动作,而printIO monad 动作,它们不兼容。这基本上就是您遇到的问题。

    【讨论】:

    • 只是一点评论。 LaTeXT_ m 只是 LaTeXT m () 的类型同义词,它本身 不是 monad 转换器。这只是一种在类型级别指定计算不返回任何内容的方法。 LaTeXT 是单子转换器,因为应用于单子 m 它会给你另一个单子,即LaTeXT m(就像你提到的IO 的情况一样)。话虽如此,很好的答案。 :)
    猜你喜欢
    • 2011-05-23
    • 2013-03-25
    • 1970-01-01
    • 2011-12-30
    • 2011-07-11
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多