相同的接口可用于不同的类型和数据

swa*_*lay 1 haskell types interface list algebraic-data-types

所以我想为我的对象typedata对象建立某种接口。所以我有

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


type Source = (Coord , Bool)
type Sink = (Coord , Bool)
Run Code Online (Sandbox Code Playgroud)

并想为所有这些接口创建一个全局接口,所以我希望该接口具有另一个字段,rotating :: Bool然后带有the Tile或the Source或the Sink

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

我首先尝试这样做而没有像这样的多余字段

data newInterface = Source | Sink | Tile | Nothing
Run Code Online (Sandbox Code Playgroud)

但是,这无效,因为我得到了Tile多次定义”错误。

我将如何解决呢?谢谢

Bar*_*icz 5

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

newtype Source = Source (Coord, Bool)
newtype Sink = Sink (Coord, Bool)
Run Code Online (Sandbox Code Playgroud)

然后,是一个存在的Typeclass反模式

class CommonInterface where
    rotating :: Bool
Run Code Online (Sandbox Code Playgroud)

以及相关的实现:

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

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

instance CommonInterface Sink where
    rotating (Sink (_, x) = x
Run Code Online (Sandbox Code Playgroud)

现在,针对该反模式的“肉”,可以创建“异构”集合:

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

instance CommonInterface Anything where
    rotating (Anything a) = rotating a
Run Code Online (Sandbox Code Playgroud)

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


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

data Anything = AnythingSink Sink | AnythingSource Source | AnythingTile Tile | AnythingNothing
Run Code Online (Sandbox Code Playgroud)

rotating然后,必须针对所有可能的情况执行以下实现:

rotating :: Anything -> Bool
rotating (AnythingSink ...) = ...
rotating (AnythingTile ...) = ...
Run Code Online (Sandbox Code Playgroud)

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