【问题标题】:Haskell type error occurs when compiling two code snippets together that separately raise no errorHaskell 类型错误发生在同时编译两个分别不引发错误的代码片段时
【发布时间】:2018-09-27 16:36:30
【问题描述】:

我遇到了 Haskell 的类型问题,下面的程序抛出编译时错误:

Couldn't match expected type ‘bytestring-0.10.8.2:Data.ByteString.Lazy.Internal.ByteString’ with actual type ‘Text’

程序是:

{-# LANGUAGE OverloadedStrings #-}
module Main where

...

import Control.Concurrent (MVar, newMVar, modifyMVar_, modifyMVar, readMVar)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Network.WebSockets as WS
import Data.Map (Map)
import Data.Aeson (decode)

...

application :: MVar ServerState -> WS.ServerApp
application state pending = do
    conn <- WS.acceptRequest pending
    msg <- WS.receiveData conn
    -- EITHER this line can be included
    T.putStrLn msg
    -- OR these two lines, but not both
    decodedObject <- return (decode msg :: Maybe (Map String Int))
    print decodedObject

...

在我看来,基本问题是putStrLn 期望Textdecode 期望Bytetring

我不明白为什么我可以运行这部分代码:

T.putStrLn msg

或者我可以运行这部分代码:

decodedObject <- return (decode msg :: Maybe (Map String Int))
print decodedObject

但不能同时使用。

在程序中解决此问题的正确方法是什么?

我猜这类似于类型强制、类型推断或其他语言中的强制转换。问题是我不知道如何清楚地表达这个问题来查找它。

好像msg 可以是多种类型之一,但是一旦它被强制为一种类型,就不能再成为另一种类型了......

我也不确定这是否与重载字符串重叠。我有编译指示并且正在使用-XOverloadedStrings进行编译

我是个新手,所以希望这是一个合理的问题。

感谢您的任何建议!谢谢

【问题讨论】:

  • 这里没有强制转换,WS.receiveData conn 的类型在其返回类型中是多态的。 Java 中的等价物是一个泛型方法,而在 C++ 中它是一个模板。不同之处在于 Java 和 C++ 都不能推断仅用作返回类型的类型参数,因此在 Java 或 C++ 中,您必须显式编写 receiveData&lt;Text&gt;(conn) 才能获得 TextreceiveData&lt;ByteString&gt;(conn) 才能获得 @ 987654335@。 Haskell 足够聪明,可以根据您对结果的处理来推断这一点,但是如果您将其同时用作 ByteString Text,则不再适用。
  • ByteString 是字节序列,Text 是字符序列。仅当您知道字节确实是文本并且正在使用哪个 encoding 时,您才能将 ByteString 转换为 Text。例如,您可以使用来自Data.Text.EncodingdecodeUtf8 :: ByteString -&gt; Text
  • 谢谢@sepp2k,@chi,这真的很有帮助,我想我现在都明白了,并在我的额外答案中总结了它

标签: haskell


【解决方案1】:

这是因为WS.receiveData 在其返回类型上是多态的:

receiveData :: WebSocketsData a => Connection -> IO a

它只需要是WebSocketsData a 实例,TextByteString 都是。所以编译器只是推断类型。

我建议你假设它是ByteString,并在使用putStrLn 时转换为Text

【讨论】:

  • 感谢 Kostiantyn,真的很有帮助。只是为了后代,我总结了我对下面的理解。非常感谢
【解决方案2】:

感谢大家的建议。我最终的理解是,Haskell 中的任何值都可以是多态的,直到你强制它确定一个类型,此时它不能是任何其他类型(愚蠢,但我之前没有看到一个明确的例子)。

在我的示例中,WS.receiveData 返回多态IO a,其中a 是类WebsocketData 的一个实例,它本身由一个可以是TextBytestring 的类型参数化。

Aeson decode 需要一个(懒惰的)Bytestring。假设我们选择(懒惰的)Bytestring 为我们的a,这意味着我之前提到的第一行需要变成:

T.putStrLn $ toStrict $ decodeUtf8 msg

将懒惰的ByteString 转换为严格的Text。只要我知道传入的 websocket 消息是 UTF8 编码的,我就可以这样做。

我可能有一些措辞有误,但基本上就是这样。

【讨论】:

  • 不完全是。值 可以 是多态的,但前提是它们是 let-bound(并且禁用了单态限制或您提供了显式类型签名)。在这种情况下,您实际上将能够将相同的值用作不同的类型,并且它永远不会停留在一个类型上。在您的情况下,msg 没有多态类型,因为它受 &lt;- 的约束,它使用 lambda 去糖到 &gt;&gt;=。所以msg 需要一个单态类型,编译器通过查看msg 在其范围的其余部分如何使用来推断。在编译器无法推断的情况下,...
  • ...您必须提供明确的类型签名。
猜你喜欢
  • 2017-10-02
  • 2018-04-03
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 2017-01-27
相关资源
最近更新 更多