我正在建造一个玩具,你可以在网格上绘制电路,我们可以模拟他们的行为。我认为抽象电路板的维度是一个有趣的实验,并尝试使代码在任何电路板尺寸(2D,3D,4D等)上工作(以类型安全的方式)。
我可以用GADT和Nats完成大部分工作;假设我使用矢量作为2D基本抽象,我们可以通过编写它来表示任何维度;
type family Count t where
Count (Compose _ g) = 1 + (Count g)
Count _ = 0
data Grid (n::Nat) a where
Grid :: f a -> Grid (Count f) a
这大部分都有用(不幸的是,类型系列需要UndecidableInstances)
有了这个,我可以表达对网格的操作在维度上保持一致,即
alter :: Grid n a -> Grid n b
棘手的一点是,我还希望允许在网格中移动。
我已经为Grid编写了一个可表示的实例,它依赖于Compose
的底层Representable,基本上你只需要为每个正在编写的仿函数配对。在我的例子中,这里有一些示例表示:
Rep (Grid 2) ~ (Sum Int, Sum Int)
Rep (Grid 3) ~ (Sum Int, (Sum Int, Sum Int))
Rep (Grid 3) ~ (Sum Int, (Sum Int, (Sum Int, Sum Int)))
等等。
还假设我们可以通过将索引与它并列为商店comonad type IGrid n a = (Rep (Grid n), Grid n a)
我写了一些在某个维度上移动的函数。从概念上讲,如果函数将焦点移动到y轴上,我们仍然可以在任何具有至少2维的维度上调用该函数:
e.g。
moveUp :: (n >= 2) => IGrid n a -> IGrid n a
当n == 2时,这是可行且简单的,但是对于更高的尺寸,通过将较低维度索引提升为较高维度索引(使用mempty填充未知维度坐标)可能最容易实现,以便我可以使用seek :: Rep (Grid n) -> Grid n a -> Grid n a
正确。
promote :: (m <= n) => Rep (Grid m) -> Rep (Grid n)
然后我可以在使用它之前将任何昏暗的索引提升为:
moveBy :: Rep (Grid n) -> IGrid n a -> IGrid n a
moveBy m (rep, grid) = (rep <> m, grid)
moveAround :: IGrid n a -> IGrid n a
moveAround grid = grid
& moveBy (promote (Sum 3, Sum 2))
& moveBy (promote (Sum 1))
我的大多数尝试都集中在使用类型类并在某些Nats上实现并使用大量类型断言。我&#39;已经 能够通过一个或两个有限的水平推广一个指数,但无法弄清楚一般情况。
我一直试图将此promote
功能写入一两个月,不时回到它,似乎可能但我无法弄明白。任何帮助将不胜感激。使用Nats和monletons lib是好的,如果这样做的话:)
感谢您花时间阅读我的困境!
答案 0 :(得分:0)
使用Nat
测量类型表达式的大小,然后尝试将一个小网格的索引注入到一个大网格的索引中,这在这里是过度的。您真正需要做的就是在修改网格之前确定要嵌套的Compose
类型的深度。
data Under g f where
Over :: Under f f
Under :: Functor h => Under g f -> Under g (Compose h f)
Under g f
的形状类似于自然数字 - 将Under (Under Over)
与S (S Z)
进行比较 - 它会告诉您Compose
需要从{{1}取消多少层f
}以便找到g
。
under :: Under g f -> (g a -> g a) -> f a -> f a
under Over f = f
under (Under u) f = Compose . fmap (under u f) . getCompose
您在评论中提到您正在使用无限拉链网格。使用嵌套Under
的{{1}}更容易,Compose
始终是左参数。我的Zipper
与Grid2
同构。
Compose Zipper Zipper
现在,在给定type Zipped = Compose Zipper
type Grid1 = Zipped Identity
type Grid2 = Zipped Grid1
zipped :: (Zipper (f a) -> Zipper (g b)) -> Zipped f a -> Zipped g b
zipped f = Compose . f . getCompose
的情况下,您可以将move :: Int -> Zipper a -> Zipper a
任意数量的Under
构造函数转移到Compose
网格的特定维度。
move
例如,要在2D网格中转到moveUnder :: Under (Zipped g) f -> Int -> f a -> f a
moveUnder u n = under u (zipped $ move n)
,您可以移动所有内部拉链。
up
现在,如果您想同时移动网格的各个维度,可以多次调用-- up :: Grid2 a -> Grid2 a
up :: Functor f => Compose f (Zipped g) a -> Compose f (Zipped g) a
up = moveUnder (Under Over) (-1)
。在这里,我将一系列moveUnder
s放入列表中。请注意,我存在量化Move
构造函数中的g
。
Move