【问题标题】:Same interface for different type and data不同类型和数据的相同接口
【发布时间】:2019-12-11 05:53:28
【问题描述】:

所以我想为我的不同typedata 对象制作某种接口。所以我有

data Tile = Tile {
  coord :: Coord,
  fixed :: Bool,
  wiel :: Bool
  } deriving (Show)


type Source = (Coord , Bool)
type Sink = (Coord , Bool)

并且想为所有这些创建一个全局接口,所以我希望该接口有另一个字段,rotating :: Bool,然后是 TileSourceSink

如果他们在哪里都实现了相同的接口,那么他们就有了额外的字段。这样我也可以将它们放在我也需要的列表中。 (甚至可能是一个Nothing 选项,以防万一我遍历列表时那里没有任何东西)。

我首先尝试在没有像这样的额外字段的情况下这样做

data newInterface = Source | Sink | Tile | Nothing

但是这不起作用,因为我收到错误 Tile defined multiple times”

我将如何解决这个问题?谢谢

【问题讨论】:

  • 为什么需要将它们放在一个列表中?你确定你需要那个吗?
  • 我想为游戏制作一个充满这些对象的二维网格

标签: list haskell types interface algebraic-data-types


【解决方案1】:

首先,这里只有两个类型定义,因为SourceSink 都只是弱别名。您需要使用newtype 来区分它们:

newtype Source = Source (Coord, Bool)
newtype Sink = Sink (Coord, Bool)

然后是Existential Typeclass Antipattern

class CommonInterface where
    rotating :: Bool

以及伴随的实现:

instance CommonInterface Tile where
    rotating (Tile _ x _) = x

instance CommonInterface Source where
    rotating (Source (_, x) = x

instance CommonInterface Sink where
    rotating (Sink (_, x) = x

现在对于那个反模式的“肉”,它可以创建一个“异构”集合:

newtype Anything = forall a. CommonInterface a => Anything a

instance CommonInterface Anything where
    rotating (Anything a) = rotating a

当然,此时您应该阅读链接的文章并理解为什么这种方法并不是最好的。话虽如此,对于反模式来说,它似乎工作得很好。


当然,如果您只想拥有一种包含上述所有内容的数据类型,那就容易多了:

data Anything = AnythingSink Sink | AnythingSource Source | AnythingTile Tile | AnythingNothing

rotating 的实现必须针对所有可能性完成:

rotating :: Anything -> Bool
rotating (AnythingSink ...) = ...
rotating (AnythingTile ...) = ...

这要简单得多,因为它需要事先知道所有可能性;在存在方法中,您可以添加更多满足CommonInterface 的类型,而无需事先了解它们。

【讨论】:

    【解决方案2】:

    您可以使用以下构造函数重命名您的类型:

    data NewInterface = Source | Sink | T Tile | None
    

    您不能使用 Nothing,因为它被 Maybe 数据类型使用

    编辑

    @Bartek Banachewicz 提供的答案涵盖了rotating 部分,我正要编辑,但他的答案涵盖了所有内容。

    【讨论】:

      【解决方案3】:

      我想我会这样做,从公共部分中分解出对每种实体来说都是特殊的数据部分:

      data Entity
          = Tile Bool -- the wiel field
          | Source
          | Sink
      
      data LocatedEntity = LocatedEntity
          { location :: Coord
          , fixed :: Bool
          , rotating :: Bool
          , entity :: Entity
          }
      

      不需要花哨的存在类型(而且你可以避免它们的反模式!),在每个调用站点都不需要大的案例分析来提取公共字段。美观轻巧。

      如果对于某些应用程序,您需要使没有LocatedEntity 成为可能,您可以根据需要使用Maybe LocatedEntity 对其进行建模。

      【讨论】:

        猜你喜欢
        • 2013-03-28
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 2013-08-14
        • 2020-03-05
        • 1970-01-01
        • 1970-01-01
        • 2015-02-05
        相关资源
        最近更新 更多