首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何识别Servant所需的扩展

如何识别Servant所需的扩展
EN

Stack Overflow用户
提问于 2020-07-31 22:07:43
回答 2查看 85关注 0票数 3

我正在读一个servant tutorial,但不知道哪个扩展用于代码的哪个部分。本教程从在文件开头添加~10个扩展名开始,但第一个示例只需要3个。当我在Servant中实现自己的服务器时,我希望使用所需的最少扩展名。有没有一种方法来确定所需的最小扩展?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-08-01 03:55:35

根据@GeorgeLyubenov的回答,让编译器告诉你你需要什么是完全允许的。在80-90%的情况下,编译器错误消息会建议扩展,如果这样可以解决问题的话。在一些情况下,它无法解决问题,您只需学习如何识别这些情况。当我在代码上工作时,我经常会添加比我需要的更多的扩展,因为我尝试了一些东西,然后放弃了它们。最后,如果我想裁剪一组扩展,我只是尝试逐个删除它们,看看编译器是否有错误(因此与George的方法相反)。这对于有一个很大的推荐扩展列表的教程或包非常有用,因为您可以将它们全部包含进来,然后尝试逐个删除它们。它有助于使用集成开发环境或编辑器模式(我使用Emacs dante),可以快速输入检查文件(例如,每次保存),因此您不必手动运行GHC 20次。

据我所知,没有任何编译器标志来转储所使用的扩展列表,所以试错方法是您所能做的最好的方法……

...unless,你实际上想要理解这些扩展的含义。这并不是说他们无缘无故地启用了编译器代码的随机位。它们支持文档齐全、易于理解的特性,虽然您可能不会理解和记住所有这些特性,但理解其中的大多数并不难。

在Servant教程中给出的列表中:

代码语言:javascript
复制
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

对于编写像这样的从属API来说,有两个非常关键:

代码语言:javascript
复制
type UserAPI1 = "users" :> Get '[JSON] [User]

最容易理解的是TypeOperators,它允许您在API类型中使用像:>这样的中缀运算符。如果没有它,您需要将API编写为:

代码语言:javascript
复制
type UserAPI1 = (:>) "users" (Get '[JSON] [User])

这在很大程度上违背了拥有一个良好的基于操作符的语法的目的。第二个关键扩展DataKinds稍难理解,但它允许您使用字符串"users" (和“滴答列表”'[...],尽管不是未滴答列表[User]JSON类型本身)作为类型。

因此,任何指定API的从属程序几乎都需要:

代码语言:javascript
复制
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}

如果你曾经写过... deriving (Generic),你就需要DeriveGeneric。在Servant程序中,如果您希望使用从您的数据类型自动派生的ToJSON实例为JSON提供服务,则最有可能出现这种情况。对于本教程中的User数据类型,实例:

代码语言:javascript
复制
instance ToJson User

要求User有一个Generic实例,您应该可以使用data User = ... deriving (Generic)自动派生该实例,这反过来又需要:

代码语言:javascript
复制
{-# LANGUAGE DeriveGeneric #-}

当您将字符串文字"whatever"用作String以外的任何东西时,都需要OverloadedStrings扩展。在Servant教程中,这首先是在编写时出现的:

代码语言:javascript
复制
{-# LANGUAGE OverloadedStrings #-}

instance Accept HTMLLucid where
    contentType _ = "text" // "html" /: ("charset", "utf-8")

在这里,///:运算符希望使用ByteString类型:

代码语言:javascript
复制
(//) :: ByteString -> ByteString -> MediaType
(//) :: MediaType -> (ByteString, ByteString) -> MediaType

如果没有OverloadedStrings扩展,您将需要提供从String文本到ByteString类型的显式转换:

代码语言:javascript
复制
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")

接下来是MultiParamTypeClassesFlexibleInstances,这是HTMLLucidMimeRender实例所需的

代码语言:javascript
复制
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

instance ToHtml a => MimeRender HTMLLucid a where
    mimeRender _ = renderBS . toHtml

当您试图定义一个接受多个参数的classinstance (或使用类约束)时,MultiParamTypeClasses扩展是一个常用的扩展。MimeRender类实际上有两个参数。第一个是用于接受的MIME类型的类型级标记,这里是HTMLLucid。第二个是将由实例呈现给该MIME类型的内容的类型。因为这是一个有两个参数的类,所以需要MultiParamTypeClasses扩展来为它编写实例。

此外,在标准的Haskell中,您只能为SomeType var1 var2 var3形式的参数(可能没有变量)编写实例。因此,您可以编写一个特定的实例,其中第一个参数的形式为SomeType,第二个参数的形式相同:

代码语言:javascript
复制
instance MimeRender HTMLLucid Int where ...

甚至在第二个参数的形式为SomeType var1的情况下

代码语言:javascript
复制
instance MimeRender HTMLLucid (Maybe var) where ...

但是,除非启用FlexibleInstances,否则第二个参数不能是普通变量a

所以,列表如下:

代码语言:javascript
复制
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}

涵盖了服务器教程所需的大部分内容。

据我所知,RankNTypes只需要写:

代码语言:javascript
复制
type (~>) m n = forall a. m a -> n a

在介绍hoistServer之前,它只用于说明一般概念,其他任何事情实际上都不需要。我也不认为服务器教程中的任何地方都需要ScopedTypeVariablesGeneralizedNewtypeDeriving

票数 5
EN

Stack Overflow用户

发布于 2020-07-31 23:45:32

我不知道还有什么比“禁用所有东西,然后根据编译器所抱怨的逐个启用它们”更好的方法。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63193423

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档