当两个结构共享某些内容时,是否有一种惯用的方法来处理这种情况?
Is there an idiomatic way to do deal with this situation when two structures share some content?
我正在制作一个玩具论坛以熟悉 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'
它在查询消息时发送的数据,其中有两个由服务器添加的附加字段,但这感觉不对,必须有一种更简洁的方法来实现这一点。
对处理此类问题的惯用方法的任何见解表示赞赏。
我会在这里考虑你的问题更像是一个概念性的问题(“当我有两种共享某种结构的数据类型时我能做什么?”)而不是简单的“我如何在 [=58= 中建模继承” ]?”已经回复 here.
要回答您的问题,您需要考虑的不仅仅是数据的结构。例如,如果我为您提供 A 和 B,并且如果我声明
data A = A Int String
data B = B Int
我怀疑你会自动假设 A
是 B
加上一个额外的 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 can be useful. And the answer to this question How do I model inheritance in Haskell? 是一个好的开始。 Lens 包提供面向对象的语法糖来处理继承关系。
但是您也可以发现 A
并不完全是 B
,但它们具有相同的祖先。你可能更愿意创建类似
的东西
data A = A { share :: C, theString :: String }
data B = B { share :: C }
data C = C Int
当您不想将 A
用作 B
时就是这种情况,但它存在一些可供两者使用的功能。实现会和前面的案例差不多,就不解释了。
最后你会发现实际上并不存在有用的关系(因此,A
和 B
之间没有共享的真正存在的函数)。那么您更愿意保留您的代码。
在您的具体情况下,我认为 Msg
和 Msg'
之间没有直接的“ 是 ”关系,因为一个是接收,另一个用于发送。但它们可以共享一个共同的祖先,因为它们都是消息。所以他们可能会有一些共同的构造函数和访问器(就 OO 编程而言)。
永远不要忘记结构总是绑定到某些函数。而范畴论告诉我们的是,你不能只看结构,还必须考虑它们的功能,才能看到彼此之间的关系。
我正在制作一个玩具论坛以熟悉 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'
它在查询消息时发送的数据,其中有两个由服务器添加的附加字段,但这感觉不对,必须有一种更简洁的方法来实现这一点。
对处理此类问题的惯用方法的任何见解表示赞赏。
我会在这里考虑你的问题更像是一个概念性的问题(“当我有两种共享某种结构的数据类型时我能做什么?”)而不是简单的“我如何在 [=58= 中建模继承” ]?”已经回复 here.
要回答您的问题,您需要考虑的不仅仅是数据的结构。例如,如果我为您提供 A 和 B,并且如果我声明
data A = A Int String
data B = B Int
我怀疑你会自动假设 A
是 B
加上一个额外的 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 can be useful. And the answer to this question How do I model inheritance in Haskell? 是一个好的开始。 Lens 包提供面向对象的语法糖来处理继承关系。
但是您也可以发现 A
并不完全是 B
,但它们具有相同的祖先。你可能更愿意创建类似
data A = A { share :: C, theString :: String }
data B = B { share :: C }
data C = C Int
当您不想将 A
用作 B
时就是这种情况,但它存在一些可供两者使用的功能。实现会和前面的案例差不多,就不解释了。
最后你会发现实际上并不存在有用的关系(因此,A
和 B
之间没有共享的真正存在的函数)。那么您更愿意保留您的代码。
在您的具体情况下,我认为 Msg
和 Msg'
之间没有直接的“ 是 ”关系,因为一个是接收,另一个用于发送。但它们可以共享一个共同的祖先,因为它们都是消息。所以他们可能会有一些共同的构造函数和访问器(就 OO 编程而言)。
永远不要忘记结构总是绑定到某些函数。而范畴论告诉我们的是,你不能只看结构,还必须考虑它们的功能,才能看到彼此之间的关系。