如何在Haskell中建模mixins /多个接口?

Rah*_*hul 10 code-reuse haskell mixins

在Haskell中遇到了关于建模继承的这个问题,它提醒我,我有一个更复杂的版本同样的问题.我将从那里采用这个例子,因为它比思考我自己更容易.

假设您的程序包含多种类型:

data Camera = Camera ...
data Light = SpotLight ... | DirectionalLight ...
data Object = Monster ... | Player ... | NPC ...
Run Code Online (Sandbox Code Playgroud)

现在你想要实现一些基本的物理,所以你希望它们都具有位置和速度,比如某种类型Vec3.

执行此操作的一种方法是Physical使用posvel函数声明类型类,并为其创建所有类型的实例.但这意味着你必须修改所有类型以包含两个Vec3s,如果你已经定义了很多很好的类型,那么这很烦人,而你只想在顶部粘合一些功能.Chris Taylor提出的基于镜头的解决方案也有同样的问题.

一个对我来说更整洁的解决方案是声明一个新类型的构造函数,

data Physical a = Physical a Vec3 Vec3
Run Code Online (Sandbox Code Playgroud)

然后你只需要执行pos,vel以及Functor实例一次,你得到所有现有的类型声明.

但是......这并不是很好.如果您现在希望能够将对象绘制为蓝色或绿色或紫色,则可能需要使用颜色执行相同操作:

data Coloured a = Coloured a Colour
Run Code Online (Sandbox Code Playgroud)

但是现在,如果你有一个Coloured Physical Camera,你必须fmap一个不同的次数取决于您是否想看看它的颜色和它的位置,或者它的焦距.和a Coloured Physical Camera应该是一样的Physical Coloured Camera,但事实并非如此.所以这不是一个优雅的解决方案.

有没有一种很好的方法可以将不同的功能集混合到Haskell中的类型中?一个简单的解决方案适用于没有语言扩展或大量样板的普通旧Haskell,这是理想的,但我也愿意学习任何与镜头相关的库,如果这是解决问题的最佳方法.

(这个关于mixins风格的代码重用的老问题似乎有关,但我担心我不完全理解这个问题或接受的解决方案.)

Nik*_*kov 1

您是否知道元数为 2 的元组有一个Functor实例,该实例映射到第二个项目?我们可以利用它来为我们谋取利益。

data PositionAndVelocity = PositionAndVelocity Vec3 Vec3
data Colour = ...

f1 :: (PositionAndVelocity, Camera) -> ...
f2 :: (Colour, Camera) -> ...
Run Code Online (Sandbox Code Playgroud)