【问题标题】:Haskell: "Couldn't match expected type IO () with IO ThreadId"Haskell:“无法将预期类型 IO () 与 IO ThreadId 匹配”
【发布时间】:2017-04-02 13:07:05
【问题描述】:

我是 Haskell 中的并发新手,我试图得到一个结果,在 main 线程中创建一个新的 MVar,将其传递给 userThread 线程,该线程从用户,然后将该值放入 M​​Var 并让main 线程打印它。

这是我目前所拥有的

module Main where

import Control.Concurrent
import Control.Monad
import System.IO
import System.Random
import Text.Printf

data Msg = C Char | Time


main :: IO ()
main = do
  hSetBuffering stdout NoBuffering
  hSetBuffering stdin NoBuffering
  hSetEcho stdin False

  -- shared resources
  chan <- newEmptyMVar
  forkIO $ userThread chan
  --r <- takeMVar chan
  --show(r)


userThread :: MVar Msg -> IO ()
userThread chan = forever $ do
  x <- getChar
  putMVar chan (C x)

现在,我一直在尝试将输入的字符放入 MVar,我已将错误粘贴在下面

assignment1.hs:20:3: error:
    * Couldn't match type `ThreadId' with `()'
      Expected type: IO ()
        Actual type: IO ThreadId
    * In a stmt of a 'do' block: forkIO $ userThread chan
      In the expression:
        do { hSetBuffering stdout NoBuffering;
             hSetBuffering stdin NoBuffering;
             hSetEcho stdin False;
             chan <- newEmptyMVar;
             .... }
      In an equation for `main':
          main
            = do { hSetBuffering stdout NoBuffering;
                   hSetBuffering stdin NoBuffering;
                   hSetEcho stdin False;
                   .... }
Failed, modules loaded: none.

任何指向正确方向的指针都会有很大帮助! 谢谢大家

【问题讨论】:

    标签: haskell concurrency io


    【解决方案1】:

    forkIO $ userThread chan 是一个返回 ThreadId 的 IO 操作,并且作为 main do 块中的最后一条语句,它使 main 也返回它。但是,您声明了 main :: IO (),所以存在类型不匹配。

    只返回一个虚拟元组。

    main = do
       ...
       forkIO $ userThread chan
       return ()
    

    (这里还有一个void 库函数,但你可以忽略它。)

    【讨论】:

    • 谢谢,这有帮助。我现在可以让程序使用deriving (Show) 编译和打印我的字符,但是,这个输出是例如C 'g',我怎样才能让它只打印用户按下的字符?
    • @SebMarsh 您可以定义自己的Show 实例,或者您可以定义一个函数Msg -&gt; StringMsg -&gt; Text,它可以打印您喜欢的消息(然后调用putStrLn 将其写入标准输出)
    • 再次感谢。当我回复 jberryman 时,我想我可以使用这样的东西来让我的 Msg 数据表现得像一个字符串,如果它是一个字符。我将如何开始这样做?我以前没有使用过自己的实例,所以我有点迷茫。
    【解决方案2】:

    由于您在学习 haskell 时会一遍又一遍地遇到此错误,因此我想为您解压:

    assignment1.hs:20:3: error:
    

    GHC 为您提供文件、行号和列号:file:line:col

        * Couldn't match type `ThreadId' with `()'
          Expected type: IO ()
            Actual type: IO ThreadId
    

    Expected type 指的是 GHC 期望表达式的类型,基于上下文,例如您提供的类型签名,或者您使用从该表达式返回的值的方式。 Actual type 是...表达式的实际类型,例如文字'c' 的类型为Charmap fst [('c', undefined)] 的实际类型为[Char],等等。

        * In a stmt of a 'do' block: forkIO $ userThread chan
    

    这个 ^ 是问题陈述。 forkIO 返回一个 ThreadId(你可以从它的类型签名中看到)。我想,这里的棘手部分是要知道为什么类型检查器期待一个类型 IO () (因为你的类型签名,以及在你的 do 块的最后一条语句中返回的值将是类型返回,正如 chi 提到的)。

    打开ghci 有助于您在开发过程中回答此类问题:

    Prelude> import Control.Concurrent
    Prelude Control.Concurrent> :t forkIO
    forkIO :: IO () -> IO ThreadId
    Prelude Control.Concurrent> :t ()
    () :: ()
    Prelude Control.Concurrent> :t repeat ()
    repeat () :: [()]
    

    如果你面前有文件,剩下的就是你可能不需要的更多上下文:

          In the expression:
            do { hSetBuffering stdout NoBuffering;
                 hSetBuffering stdin NoBuffering;
                 hSetEcho stdin False;
                 chan <- newEmptyMVar;
                 .... }
          In an equation for `main':
              main
                = do { hSetBuffering stdout NoBuffering;
                       hSetBuffering stdin NoBuffering;
                       hSetEcho stdin False;
                       .... }
    Failed, modules loaded: none.
    

    【讨论】:

    • 谢谢,这帮助我稍微理解了我的问题,但现在我面临一个全新的问题,即如何使我的 Msg 类型表现得像一个字符串。如果键入的 char 不在列表中,我想将它放在现有列表中,但如果 char 已存在于此列表中,则不会添加它,并且从列表中删除该 char 的所有实例。在进行更多研究后不久,我将提出另一个问题。
    猜你喜欢
    • 2021-03-24
    • 2017-09-16
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多