我正在制作一個玩具論壇來熟悉 Haskell 和 Servant。
我的 API 看起來像這樣:
type UserAPI = "messages" :> ReqBody '[JSON] Msg :> Header "X-Real-IP" String :> Post '[JSON] APIMessage
:<|> "messages" :> ReqBody '[JSON] Int :> Get '[JSON] [Msg']
我的型別看起來像這樣:
data Msg = Msg
{ thread :: Int
, dname :: String
, contents :: String
} deriving (Eq, Show, Generic)
data Msg' = Msg'
{ thread' :: Int
, stamp' :: UTCTime
, dname' :: String
, contents' :: String
, ip' :: String
} deriving (Eq, Show, Generic)
并且他們派生ToJSON//實體FromJSON,FromRow非常方便。
Msg表示 API 在接收訊息時期望Msg'的資料和它在查詢訊息時發送的資料,其中有兩個由服務器添加的附加欄位,但這感覺不對,必須有更清潔的方法來實作這一點。
任何有關處理此類問題的慣用方法的見解都值得贊賞。
uj5u.com熱心網友回復:
我將在這里考慮您的問題更多的是一個概念問題(“當我有兩種共享某種結構的資料型別時我能做什么?”)而不是簡單的“如何在 Haskell 中建模繼承?” 這里已經回復了。
要回答您的問題,您需要考慮的不僅僅是資料的結構。例如,如果我向您提供 A 和 B 并且如果我宣告
data A = A Int String
data B = B Int
我懷疑您是否會自動假設 aA是B帶有額外的 a String。您可能會嘗試弄清楚這兩種資料結構之間的確切關系。這是一件好事。
如果每個實體A實際上都可以看作是一個實體,B那么提供在代碼中表示它的方法可能是相關的。然后你可以使用一個普通的 Haskell 方式
data A = A { super :: B, theString :: String }
data B = B { id :: Int }
顯然,如果不創建一些其他函式,使用這些資料型別將不容易。例如,一個fromB功能可能是相關的
fromB :: B -> String -> A
toB :: A -> B
并且還可以使用 typeclass 來訪問id
class HasId a where
getId :: a -> Int
instance HasId A where
getId = id . super
這就是Lens的一些幫助有用的地方。以及這個問題的答案如何在 Haskell 中建模繼承?是一個好的開始。Lens 包提供了面向物件的語法糖來處理繼承關系。
但是,您也可以發現 aA不完全是 a B,但它們都具有相同的祖先。你可能更喜歡創建類似的東西
data A = A { share :: C, theString :: String }
data B = B { share :: C }
data C = C Int
當您不想將 aA用作 a時就是這種情況B,但它存在一些可供兩者使用的功能。實作將接近前面的案例,所以我不解釋它。
最后,您會發現實際上并不存在有用的關系(因此,沒有真正存在的函式在Aand之間共享B)。那么您更愿意保留您的代碼。
在您的具體情況下,我認為和之間沒有直接的“是”關系Msg,Msg'因為一個用于接收,另一個用于發送。但是它們可以共享一個共同的祖先,因為兩者都是訊息。所以他們可能會有一些共同的建構式和訪問器(就OO編程而言)。
永遠不要忘記結構總是系結到某些功能。范疇論告訴我們的是,你不能只看結構,還必須考慮它們的功能,才能看到彼此之間的關系。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/487879.html
