您可以組合到這些更具描述性的數據類型:
data Config = Config
{ cKey :: Key
, cAccount :: Account
}
那麼也許有type
S或newtypes
使其他參數的詳細描述:
-- I have no idea what these actually should be, I'm just making up something
type Count = Int
type Name = String
type Position = (Int, Int)
myApiFunction :: Config -> Count -> Name -> Position -> IO MyType
myApiFunction conf count name (x, y) =
myPreviousApiFunction (cKey conf)
(cAccount conf)
name
name
x
y
如果總是需要Config
,那麼我會建議在一個Reader
monad中工作,你可以很容易地做
myApiFunction
:: (MonadReader Config io, MonadIO io)
=> Count -> Name -> Position
-> io MyType
myApiFunction count name (x, y) = do
conf <- ask
liftIO $ myPreviousApiFunction
(cKey conf)
(cAccount conf)
name
name
x
y
這使用單數變壓器的mtl
庫。如果你不希望有遍地鍵入約束,您還可以使用ConstraintKinds
擴展它的別名:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
...
type ApiCtx io = (MonadReader Config io, MonadIO io)
...
myApiFunction
:: ApiCtx io
=> Count -> Location -> Position
-> io MyType
myApiFunction ...
根據您的具體應用,你也可以把它分解成多個功能。我見過很多的API在此之前,有這樣的事情
withCount :: ApiCtx io => Count -> io a -> io a
withName :: ApiCtx io => Name -> io a -> io a
withPos :: ApiCtx io => Position -> io a -> io a
(&) :: a -> (a -> b) -> b
request :: ApiCtx io => io MyType
> :set +m -- Multi-line input
> let r = request & withCount 1
| & withName "foo"
| & withPos (1, 2)
> runReaderT r (Config key acct)
這些只是技術了一把,也有其他人在那裏很好,但他們普遍開始後成爲這個更復雜。其他人對於如何做到這一點會有不同的偏好,我相信很多人會不同意我的看法,其中有些甚至是不錯的做法(特別是ConstraintKinds
,這是不被普遍接受的)。
如果您發現自己的類型簽名太大了,即使在應用了這些技術之後,也許您會從錯誤的方向接近問題,也許這些功能可以分解爲更簡單的中間步驟,也許這些參數中的一些可以邏輯分組到更具體的數據類型中,也許你只需要一個更大的記錄結構來處理設置複雜的操作。它現在是非常開放的。
聲明一個將所有這些獨立參數綁定到一個值的類型? (嘗試查找邏輯分組通常是有趣的部分。)您可能還想爲所有這些「Int」值定義一個「newtype」或至少一個「type」別名。 – MathematicalOrchid
我在Strive中嘗試了一堆不同的想法,我的API綁定到Strava。我最終結束了設置類型和鏡頭。看看[這個問題](https://github.com/tfausak/strive/issues/44)的一些討論和[這個例子](https://github.com/tfausak/strive/tree/v1.0。1#細分 - 排行榜),看看它現在的樣子。 –