我正在读一个servant tutorial,但不知道哪个扩展用于代码的哪个部分。本教程从在文件开头添加~10个扩展名开始,但第一个示例只需要3个。当我在Servant中实现自己的服务器时,我希望使用所需的最少扩展名。有没有一种方法来确定所需的最小扩展?
发布于 2020-08-01 03:55:35
根据@GeorgeLyubenov的回答,让编译器告诉你你需要什么是完全允许的。在80-90%的情况下,编译器错误消息会建议扩展,如果这样可以解决问题的话。在一些情况下,它无法解决问题,您只需学习如何识别这些情况。当我在代码上工作时,我经常会添加比我需要的更多的扩展,因为我尝试了一些东西,然后放弃了它们。最后,如果我想裁剪一组扩展,我只是尝试逐个删除它们,看看编译器是否有错误(因此与George的方法相反)。这对于有一个很大的推荐扩展列表的教程或包非常有用,因为您可以将它们全部包含进来,然后尝试逐个删除它们。它有助于使用集成开发环境或编辑器模式(我使用Emacs dante),可以快速输入检查文件(例如,每次保存),因此您不必手动运行GHC 20次。
据我所知,没有任何编译器标志来转储所使用的扩展列表,所以试错方法是您所能做的最好的方法……
...unless,你实际上想要理解这些扩展的含义。这并不是说他们无缘无故地启用了编译器代码的随机位。它们支持文档齐全、易于理解的特性,虽然您可能不会理解和记住所有这些特性,但理解其中的大多数并不难。
在Servant教程中给出的列表中:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}对于编写像这样的从属API来说,有两个非常关键:
type UserAPI1 = "users" :> Get '[JSON] [User]最容易理解的是TypeOperators,它允许您在API类型中使用像:>这样的中缀运算符。如果没有它,您需要将API编写为:
type UserAPI1 = (:>) "users" (Get '[JSON] [User])这在很大程度上违背了拥有一个良好的基于操作符的语法的目的。第二个关键扩展DataKinds稍难理解,但它允许您使用字符串"users" (和“滴答列表”'[...],尽管不是未滴答列表[User]或JSON类型本身)作为类型。
因此,任何指定API的从属程序几乎都需要:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}如果你曾经写过... deriving (Generic),你就需要DeriveGeneric。在Servant程序中,如果您希望使用从您的数据类型自动派生的ToJSON实例为JSON提供服务,则最有可能出现这种情况。对于本教程中的User数据类型,实例:
instance ToJson User要求User有一个Generic实例,您应该可以使用data User = ... deriving (Generic)自动派生该实例,这反过来又需要:
{-# LANGUAGE DeriveGeneric #-}当您将字符串文字"whatever"用作String以外的任何东西时,都需要OverloadedStrings扩展。在Servant教程中,这首先是在编写时出现的:
{-# LANGUAGE OverloadedStrings #-}
instance Accept HTMLLucid where
contentType _ = "text" // "html" /: ("charset", "utf-8")在这里,//和/:运算符希望使用ByteString类型:
(//) :: ByteString -> ByteString -> MediaType
(//) :: MediaType -> (ByteString, ByteString) -> MediaType如果没有OverloadedStrings扩展,您将需要提供从String文本到ByteString类型的显式转换:
import qualified Data.ByteString.Char8 as C
instance Accept HTMLLucid where
contentType _ = C.pack "text" // C.pack "html" /: (C.pack "charset", C.pack "utf-8")接下来是MultiParamTypeClasses和FlexibleInstances,这是HTMLLucid的MimeRender实例所需的
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
instance ToHtml a => MimeRender HTMLLucid a where
mimeRender _ = renderBS . toHtml当您试图定义一个接受多个参数的class或instance (或使用类约束)时,MultiParamTypeClasses扩展是一个常用的扩展。MimeRender类实际上有两个参数。第一个是用于接受的MIME类型的类型级标记,这里是HTMLLucid。第二个是将由实例呈现给该MIME类型的内容的类型。因为这是一个有两个参数的类,所以需要MultiParamTypeClasses扩展来为它编写实例。
此外,在标准的Haskell中,您只能为SomeType var1 var2 var3形式的参数(可能没有变量)编写实例。因此,您可以编写一个特定的实例,其中第一个参数的形式为SomeType,第二个参数的形式相同:
instance MimeRender HTMLLucid Int where ...甚至在第二个参数的形式为SomeType var1的情况下
instance MimeRender HTMLLucid (Maybe var) where ...但是,除非启用FlexibleInstances,否则第二个参数不能是普通变量a。
所以,列表如下:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}涵盖了服务器教程所需的大部分内容。
据我所知,RankNTypes只需要写:
type (~>) m n = forall a. m a -> n a在介绍hoistServer之前,它只用于说明一般概念,其他任何事情实际上都不需要。我也不认为服务器教程中的任何地方都需要ScopedTypeVariables或GeneralizedNewtypeDeriving。
发布于 2020-07-31 23:45:32
我不知道还有什么比“禁用所有东西,然后根据编译器所抱怨的逐个启用它们”更好的方法。
https://stackoverflow.com/questions/63193423
复制相似问题