当两个结构共享某些内容时,是否有一种惯用方法可以处理这种情况?

发布于 2025-02-03 19:39:10 字数 829 浏览 4 评论 0原文

我正在做一个玩具论坛,以熟悉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'在查询消息时它发送的数据,该数据包含服务器添加的其他两个字段,但是此感觉不正确,必须有一种更干净的方法来实现这一目标。

对惯用方式处理这种问题的任何洞察力。

I'm making a toy forum to gain familiarity with Haskell and Servant.

My API looks something like this:

type UserAPI = "messages" :> ReqBody '[JSON] Msg :> Header "X-Real-IP" String :> Post '[JSON] APIMessage
               :<|> "messages" :> ReqBody '[JSON] Int :> Get '[JSON] [Msg']

My types look something like this:

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)

and they derive ToJSON / FromJSON / FromRow instances, which is very convenient.

Msg represents the data the API expects when receiving messages and Msg' the data it sends when queried for messages, which has two additional fields that are added by the server, but this doesn't feel right and there has to be a cleaner way to achieve this.

Any insight on an idiomatic way to do deal with this sort of problem appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

跨年 2025-02-10 19:39:10

我会在这里考虑,您的问题更多是一个概念上的(“当我有两个共享某种结构的数据类型时,我该怎么办?”),而不是简单的“我如何建模Haskell中的继承?”已经回复了在这里

要回答您的问题,您将不仅要考虑数据的结构。例如,如果我向您提供A和B,并且如果我说

data A = A Int String
data B = B Int 

我怀疑您会自动假设A a是A b带有额外的字符串。您可能会尝试确定这两个数据结构之间的确切关系。这是一件好事。

如果a的每个实例实际上都可以看作是b的实例,则可以在代码中提供表示它是相关的。然后,您可以显然使用普通的Haskell方式

data A = A { super :: B, theString :: String }
data B = B { id :: Int }

,而在没有创建其他功能的情况下,使用这些数据类型并不容易。例如,frofb函数可能是相关的

fromB :: B -> String -> A 
toB   :: A -> B

,您还可以使用Typeclass访问ID

class HasId a where
    getId :: a -> Int

instance HasId A where
    getId = id . super 

这是某些帮助表格镜头很有用。和此问题的答案我如何在Haskell中建模sashitance?是一个好的开始。镜头软件包提供面向对象的句法糖来处理遗传关系。

但是,您还可以发现a并不完全是b,但它们都共享同一祖先。 您可能更喜欢创建类似的东西

data A = A { share :: C, theString :: String }
data B = B { share :: C }
data C = C Int

当您不想将a用作b的情况下, ,但是它存在着两者都可以使用的功能。实施将接近以前的情况,因此我不解释。

最后,您可能会发现,实际上并不存在可能有用的关系(因此,没有真正存在ab之间的函数。然后,您希望保留代码。

在您的具体情况下,我认为msgmsg'之间没有直接的“ ”是另一个是用于发送的。但是他们可以共享一个共同的祖先,因为两者都是消息。因此,他们可能会有一些共同的构造函数(按OO编程)。

尝试永远不要忘记该结构总是与某些功能结合。哪种类别理论教给我们的是,您不仅要看结构,而且还必须考虑它们的功能,以了解彼此之间的关系。

I will consider here that you question is more a conceptual one ("What can I do when I have two data types that share some structure ?") than a simple "How do I model inheritance in Haskell ?" that is already replied here.

To answer your question, you will need to consider more than just the structure of your data. For example, if I provide you A and B and if I state that

data A = A Int String
data B = B Int 

I doubt that you will automatically make the assumption that a A is a B with an extra String. You will probably try to figure the exact relation between these two data structure. And this is the good thing to do.

If each instance of A can actually be seen as an instance of B then it can be relevant to provide way to represent it in your code. Then you could use a plain Haskell way with a

data A = A { super :: B, theString :: String }
data B = B { id :: Int }

Obviously, this will not be easy to work with these datatype without creating some other functions. For example a fromB function could be relevant

fromB :: B -> String -> A 
toB   :: A -> B

And you can also use typeclass to access id

class HasId a where
    getId :: a -> Int

instance HasId A where
    getId = id . super 

This is where some help form Lens can be useful. And the answer to this question How do I model inheritance in Haskell? is a good start. Lens package provides Object Oriented syntactic sugar to handle inheritance relationship.

However you can also find that a A is not exactly a B but they both share the same ancestor. And you could prefer to create something like

data A = A { share :: C, theString :: String }
data B = B { share :: C }
data C = C Int

This is the case when you do not want to use a A as a B, but it exists some function that can be used by both. The implementation will be near the previous cases, so I do not explain it.

Finally you could find that there does not really exists relation that can be useful (and, therefore, no function that will really exists that is shared between A and B). Then you would prefer to keep your code.

In your specific case, I think that there is not a direct "is a" relation between Msg and Msg' since one is for the receiving and the other is for the sending. But they could share a common ancestor since both are messages. So they will probably have some constructors in common and accessors (in term of OO programming).

Try to never forget that structure is always bind to some functions. And what category theory teaches us is that you cannot only look at the structures only but you have to consider their functions also to see the relation between each other.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文