`-XOverloadedStrings`如何工作? (在Yesod中)

时间:2016-04-03 10:36:59

标签: string haskell text yesod

我在他们的官方yesod书中关注Yesod教程。 (http://www.yesodweb.com/book/basics

不幸的是,他们关于这本书的教程不会起作用,更糟糕的是它的输出是非常神秘的消息,关于不算数的类型让我花了很长时间才能理解。这是他们的原始代码和错误消息,由代码生成:

{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import           Yesod

data Links = Links

mkYesod "Links" [parseRoutes|
/ HomeR GET
|]

instance Yesod Links

getHomeR = return $ object ["msg" .= "Hello World"]

main :: IO ()
main = warp 3000 Links

错误:

helloworld2.hs:18:36:
    No instance for (ToJSON a0) arising from a use of ‘.=’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance ToJSON a => ToJSON (Control.Applicative.Const a b)
        -- Defined in ‘Data.Aeson.Compat’
      instance ToJSON (Data.Proxy.Proxy a)
        -- Defined in ‘Data.Aeson.Compat’
      instance ToJSON Data.Version.Version
        -- Defined in ‘Data.Aeson.Compat’
      ...plus 7 others
    In the expression: "msg" .= "Hello World"
    In the first argument of ‘object’, namely
      ‘["msg" .= "Hello World"]’
    In the second argument of ‘($)’, namely
      ‘object ["msg" .= "Hello World"]’

helloworld2.hs:18:40:
    No instance for (Data.String.IsString a0)
      arising from the literal ‘"Hello World"’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Data.String.IsString Value
        -- Defined in ‘aeson-0.9.0.1:Data.Aeson.Types.Internal’
      instance (a ~ Data.ByteString.Internal.ByteString) =>
               Data.String.IsString
                 (attoparsec-0.13.0.1:Data.Attoparsec.ByteString.Internal.Parser a)
        -- Defined in ‘Data.Attoparsec.ByteString.Char8’
      instance (a ~ Data.Text.Internal.Text) =>
               Data.String.IsString
                 (attoparsec-0.13.0.1:Data.Attoparsec.Text.Internal.Parser a)
        -- Defined in ‘attoparsec-0.13.0.1:Data.Attoparsec.Text.Internal’
      ...plus 9 others
    In the second argument of ‘(.=)’, namely ‘"Hello World"’
    In the expression: "msg" .= "Hello World"
    In the first argument of ‘object’, namely
      ‘["msg" .= "Hello World"]’

似乎object ["msg" .= "Hello World"]是问题所在。 GHC不了解"msg""Hello World"的类型,也无法构建JSON object。我必须明确表达他们的类型:

import           Data.Text (Text)
getHomeR = return $ object [("msg" :: Text) .= ("Hello World" :: Text)]

似乎AesonText类型的JSON实例,我听说大部分文字处理代码都使用Text类型,而不是String ([Char])出于效率原因。但是,如果每个双引号代码("msg""Hello World")被自动解析为Text而不是String,那么它会不会很好?我认为OverloadedStrings编译指示告诉编译器完全按照这样做("重载"将字符串转换为Text),但是上面没有类型签名的错误表明我错了

当然,如果我必须为我为输出编写的每个字符串提供:: Text类型签名,那将是非常繁琐和繁琐的 - 是否有任何解决方案,或者我只是不要理解足以编写Haskell和Yesod代码?

3 个答案:

答案 0 :(得分:2)

使用OverloadedStrings时,显式字符串的处理方式与显式数字类似。所以

case DragEvent.ACTION_DROP:
    final View droppedView = (View) event.getLocalState();
    ViewGroup owner = (ViewGroup) droppedView.getParent();
    owner.post(new Runnable(){
        @Override
        public void run() {
            owner.removeView(droppedView);
        }
    });
}

被贬低为

x = "foo"

IsString类定义了“fromString”函数,所以现在所有类型检查器都知道

x = fromString "foo"

因此,这就是编译器抱怨类型不明确的原因。有几种不同的字符串类型与ToJSON实例,编译器抱怨它不知道选择哪一个。

我担心的唯一解决方案是丢失OverloadedStrings或输入显式类型注释以告诉编译器选择哪个实例。

On String vs. Text:当你使用像这样的短常量字符串时,效率不是一个特别大的问题。如果你正在批量处理文本,那么它是一个更大的问题,而且在某些语言中,String = [Char]的Haskell概念也会崩溃。文本正确处理这些,但String不能。因此,如果您需要将代码国际化,那么当您尝试执行诸如大写单词之类的操作时,String会向您显示模糊的问题。

答案 1 :(得分:2)

您正在查看的JSON代码段确实可以直接在本书中正常工作。在它的顶部还有一个额外的pragma,你的代码中缺少这个pragma:

getHomeR  = return $ object ["msg" .= ("Hello World" :: String)]

我并不是代码片段的忠实粉丝,因为我非常确定人们不会在生产代码中使用该pragma。编写像这样的代码更典型:

ExtendedDefaultRules

请注意,GHCi使用object ["msg" .= "Hello World"]可以更轻松地将表达式输入REPL,而无需指定其类型。如果您在GHCi中输入ghci > object ["msg" .= "Hello World"] <interactive>:2:18: Warning: Defaulting the following constraint(s) to type ‘String’ (IsString a0) arising from the literal ‘"Hello World"’ at <interactive>:2:18-30 (ToJSON a0) arising from a use of ‘.=’ at <interactive>:2:15-16 In the second argument of ‘(.=)’, namely ‘"Hello World"’ In the expression: "msg" .= "Hello World" In the first argument of ‘object’, namely ‘["msg" .= "Hello World"]’ <interactive>:2:18: Warning: Defaulting the following constraint(s) to type ‘String’ (IsString a0) arising from the literal ‘"Hello World"’ at <interactive>:2:18-30 (ToJSON a0) arising from a use of ‘.=’ at <interactive>:2:15-16 In the second argument of ‘(.=)’, namely ‘"Hello World"’ In the expression: "msg" .= "Hello World" In the first argument of ‘object’, namely ‘["msg" .= "Hello World"]’ Object (fromList [("msg",String "Hello World")]) ,它会告诉您它的默认值是什么:

Class CommunicationThread extends Thread {

  //Vector containing all client sockets currently connected
  //Held offsite, here for clarity
  public Vector<Socket> socketVector;

  public CommunicationThread (Socket clientSoc, Server ec3, Vector<Socket>socketVectorg)
  {
    //add new socket to vector, start thread
    clientSocket = clientSoc;
    socketVectorg.add(clientSocket);
    this.socketVector = socketVectorg;
    gui = ec3;
  }

  public void run()
  {
    System.out.println ("New Communication Thread Started");

    try {
        //Client's chat box (output)
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),
                true);

        //Input line from client
        BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()));

        String inputLine;

        while ((inputLine = in.readLine()) != null) {
            System.out.println("Server: " + inputLine);
            gui.history.insert(inputLine + "\n", 0);

            //*************HERE IS MY ISSUE*******************
            for(Socket s : socketVector){
                out = new PrintWriter(s.getOutputStream(),
                        true);
                out.println(inputLine);
            }

            if (inputLine.equals("Bye."))
                break;

            if (inputLine.equals("End Server."))
                gui.serverContinue = false;

        }

        out.close();
        in.close();
        clientSocket.close();
    } 
    catch (IOException e) 
    {
     System.err.println("Problem with Communication Server");
     //System.exit(1); 
    } 
  }
} 

答案 2 :(得分:1)

objectPair值列表作为参数。 Pairtype Pair = (Text, Value),它是tye KeyValue类型类的实例,它提供了方便构造函数(.=) :: ToJSON v => Text -> v -> kv

问题如下:.=要求值类型具有ToJSON实例,但不会强制调用者进入任何具体类型。

同时,来自IsString的{​​{3}}函数在其返回类型上重载:fromString :: String -> a。调用的精确实现由返回类型确定。使用OverloadedStrings时,会为字符串文字隐式调用fromString

如果我们直接将fromString的结果作为.=的值参数提供,则编译器没有足够的信息来为该值指定具体类型。它应该创建Text值并将其转换为json吗?或者也许创建一个ByteString并将其转换为json?问题类似于show . read等含糊不清的作品。

就个人而言,我不是使用类型注释,而是使用Value类型的fromString构造函数包装字符串文字来解决歧义。它告诉编译器文字将是Text,当然ValueToJSON的实例。这比类型注释要简洁一些:

foo :: Value
foo = object ["foo" .= String "faa"]

您还可以定义.=的特化,其中以Text为具体值:

(.=|) :: KeyValue kv => Text -> Text -> kv 
(.=|) = (.=)

foo :: Value
foo = object ["foo" .=| "faa"]