【问题标题】:servant a functional dependency error with `enter`使用`enter`服务一个功能依赖错误
【发布时间】:2017-02-18 14:35:32
【问题描述】:

我稍微改变了仆人教程中显示的应用程序,使Reader monad 成为ReaderT,就像这样

{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE PolyKinds                  #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE TypeOperators              #-}

module Lib
    ( runServer
    ) where

import           Control.Monad.Except
import           Control.Monad.Reader
import qualified Data.Text                as T
import           Network.Wai
import           Servant

type WebApi
  =    "static" :> Raw
  :<|> "foo" :> Get '[PlainText] T.Text

type Foo = String

server :: ServerT WebApi (ReaderT Foo (ExceptT ServantErr IO))
server = static :<|> foo
  where
    static :: Application
    static = undefined

    -- Handler T.Text
    foo :: ReaderT Foo (ExceptT ServantErr IO) T.Text
    foo = undefined

webAPI :: Proxy WebApi
webAPI = Proxy

readerToHandler :: Foo -> ReaderT Foo (ExceptT ServantErr IO) :~> ExceptT ServantErr IO
readerToHandler t = Nat (\x -> runReaderT x t)

-- readerServer :: ServerT WebApi (ExceptT ServantErr IO)
-- readerServer = enter (readerToHandler "foobarbaz") server

-- serve' :: Application
-- serve' = serve webAPI server

runServer :: IO ()
runServer = return ()

问题是我无法启用readerServer 功能,类型检查失败并出现这个难以理解的错误

src/Lib.hs:45:16: error:
    • Couldn't match type ‘IO’ with ‘ExceptT ServantErr IO’
        arising from a functional dependency between:
          constraint ‘Servant.Utils.Enter.Enter
                        (IO ResponseReceived)
                        (ReaderT Foo (ExceptT ServantErr IO) :~> ExceptT ServantErr IO)
                        (IO ResponseReceived)’
            arising from a use of ‘enter’
          instance ‘Servant.Utils.Enter.Enter (m a) (m :~> n) (n a)’
            at <no location info>
    • In the expression: enter (readerToHandler "foobarbaz") server
      In an equation for ‘readerServer’:
          readerServer = enter (readerToHandler "foobarbaz") server
Failed, modules loaded: none.

有什么想法吗?

【问题讨论】:

    标签: haskell servant


    【解决方案1】:

    问题在于Raw 端点的存在,它与enter 的交互效果不佳。这是Servant中的known annoyance

    Enter 类型类决定了可以转换哪些处理程序集以及进行哪些转换。它有三个实例:

    • Enter (m a) ((:~&gt;) m n) (n a) 最简单的情况。如果您有一个单子动作和一个将其带到不同单子的自然转换,则可以应用该转换。

    • Enter b arg ret =&gt; Enter (a -&gt; b) arg (a -&gt; ret)。如果您有一个带参数的处理程序,并且您知道如何转换处理程序的最终一元操作,则可以使用相同的arg 转换来转换处理程序。

    • (Enter typ1 arg1 ret1, Enter typ2 arg2 ret2, (~) * arg1 arg2) =&gt; Enter ((:&lt;|&gt;) typ1 typ2) arg1 ((:&lt;|&gt;) ret1 ret2) 如果您有一个组合 :&lt;|&gt; 处理程序,并且每个处理程序可以使用 same 自然变换 arg1 单独变换,那么您可以使用 @ 变换组合987654333@也一样。

    在您的示例中,最后一个条件失败,因为Raw 的处理程序具有类型Application,即Request -&gt; (Response -&gt; IO ResponseReceived) -&gt; IO ResponseReceived,而另一个处理程序是ReaderT Foo (ExceptT ServantErr IO) 操作。类型不匹配,因此组合没有 Enter 实例。


    有一个workaround:在您的自定义处理程序上调用enter,然后才使用Application 处理程序组合它们。

    type WebApi
      =    "static" :> Raw
      :<|> FooEndpoint
    
    type FooEndpoint = "foo" :> Get '[PlainText] T.Text
    
    readerServer :: ServerT WebApi (ExceptT ServantErr IO)
    readerServer = static :<|> enter (readerToHandler "foobarbaz") foo
      where
        static :: Application
        static = undefined
        foo :: ReaderT Foo (ExceptT ServantErr IO) T.Text
        foo = undefined
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-01
      • 2019-10-19
      • 1970-01-01
      • 2019-05-18
      • 2018-09-30
      • 1970-01-01
      相关资源
      最近更新 更多