2017-02-18 93 views
3

函數依賴誤差我略有改變的僕人教程中所示,使Reader單子一個ReaderT該應用,像這樣僕人用`enter`

{-# 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. 

任何想法發生了什麼問題?

回答

2

問題是存在Raw端點,該端點與enter不協調。這是僕人中的一個known annoyance

類型類別Enter確定可以轉換哪些處理程序集以及哪些轉換。它有三種情況:

  • Enter (m a) ((:~>) m n) (n a)最簡單的情況。如果你有一個monadic動作和一個自然轉換,將它轉換到另一個monad,你可以應用這個轉換。

  • Enter b arg ret => Enter (a -> b) arg (a -> ret)。如果您有一個接受參數的處理程序,並且您知道如何轉換處理程序的最終monadic動作,則可以使用相同的arg轉換來轉換處理程序。

  • (Enter typ1 arg1 ret1, Enter typ2 arg2 ret2, (~) * arg1 arg2) => Enter ((:<|>) typ1 typ2) arg1 ((:<|>) ret1 ret2)如果有處理程序的組合物:<|>和每個處理程序可單獨使用相同天然轉化arg1被變換,那麼就可以改造組合物與arg1爲好。

這最後一個條件在你的榜樣失敗,因爲Raw處理程序的類型爲Application,這是Request -> (Response -> IO ResponseReceived) -> 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 
相關問題