【问题标题】:Haskell: how to use haskeline and write to a file in the same programHaskell:如何使用 haskeline 并在同一个程序中写入文件
【发布时间】:2019-08-09 07:59:16
【问题描述】:

我在 Haskell 中编写了一个程序,它在当前目录中构建一个吉他标签作为 txt 文件。它从用户那里获取一串和弦,然后构建正确的输出并将其逐行写入文件。

当我使用 getLine 时,我无法在输入中使用退格键,因为它会在屏幕上打印一堆乱码。

我正在尝试使用 haskeline 来解决这个问题,同时我注释掉了我的主要方法的大部分,以便每次更改都需要更少的编辑(我在 'main' 中注释掉的每个命令都与我保留的单个命令,所以如果我能让这个简化版本工作,整个事情应该工作)。基本上,我需要能够使用 haskeline 从用户那里获取输入,但之后我还需要在我的“do”块中运行一些“副作用”命令。

我是 Haskell 的新手,我不完全理解什么是允许的,什么是不允许的,或者为什么。这是我的程序的简化版本:

import Data.List
import System.Console.Haskeline

main = runInputT defaultSettings loop
 where
   loop :: InputT IO ()
   loop  = do
     name <- getInputLine "Enter name of song: "
     case name of
       Nothing -> return ()
       Just songName -> return ()
     chords <- getInputLine "Enter chords to be tabified "
     case chords of
       Nothing -> do outputStrLn $ "No chords entered. Exiting."
       Just chords -> do
                        writeFile "./test.txt" "did it work?"
                        return ()

我直接从 Haskeline 教程中获得了所有这些语法。我尝试在不先进行任何更改的情况下运行它并且它有效,所以我知道这一切都是正确的 - 除了我编辑的最后 3 行,我有“do”块并试图在“之前”调用“writeFile”返回()”。

我知道“循环”的类型必须是 InputT IO () 才能使用 getInputLine(getLine 的 haskeline 版本),但我不知道如何完成像写入文件这样的“副作用”同时。

当我尝试在 ghci 中加载我的项目时,我收到以下错误:

error:
-Couldn't match type 'IO' with 'InputT IO'
 Expected type: InputT IO ()
   Actual type: IO ()
- In a stmt of a 'd' block: writeFile "./test.txt" "did it work?"
  In the expression:
    do { writeFile "./test.txt" "did it work?";
         return () }
  In a case alternative:
    Just chords
      -> do { writeFile "./test.txt" "did it work?";
              return () }

Failed, modules loaded: none.

【问题讨论】:

    标签: haskell io functional-programming monads haskeline


    【解决方案1】:

    由于InputT IOMonadIO 的一个实例,您可以通过将其提升到InputT IO 操作来运行任何IO 操作,使用

    liftIO :: IO a -> InputT IO a
    

    确实,这是在支持 IO 但不支持 IO 的 moand 中“运行 IO”的标准方式。

    【讨论】:

    • 嗨@chi,非常感谢您的解释。我仍然无法从上面运行我的示例;我想我以某种方式弄乱了语法。如果不是太麻烦的话,你能告诉我如何将它整合到我的程序中吗?
    • @BabaSvoloch 您需要检查您的操作类型。例如。 writeFile ... 返回IO (),所以需要使用liftIO (writeFile ...) 将其变成InputT IO ()。相反,getInputLine ... 应该已经可以了——不需要提升。
    • 谢谢,我搞定了!我的问题是,当我尝试使用“liftIO”时,ghci 建议我指的是 haskeline 库中的“liftIOOp”,我认为它是相同的功能,并且 haskeline 库最近已更新。我没有意识到 liftIO 不是来自 haskeline 库,我需要导入 Control.Monad.IO.Class
    【解决方案2】:

    InputT 是 MonadTrans 的一个实例,所以

    Just chords -> lift $ do
    

    编辑:

    liftControl.Monad.Trans.Class 中。 (帽子提示:Jon Purdy

    【讨论】:

    • 我尝试将它添加到我的项目中,在“writeFile”和“return ()”行之前,我收到此错误:“变量不在范围内:lift :: IO () -> InputT IO()"
    • 我想也许这个错误意味着我需要导入一个库来使用“lift”,但我到处找了,我找不到它。我发现了一些像 Control.Applicative 这样的库,但它使用的是一种叫做“LiftA”的东西,我怀疑它是一回事。你有什么想法吗?
    • @BabaSvoloch:对于lift,您需要来自transformersControl.Monad.Trans.Class;对于liftIO,它是Control.Monad.IO.Class,它曾经位于transformers,但现在位于base。使用Hoogle,您可以更轻松地找到这些内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-28
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 2016-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多