抽象类型的维度

时间:2017-09-09 17:17:25

标签: haskell type-promotion singleton-type

我正在建造一个玩具,你可以在网格上绘制电路,我们可以模拟他们的行为。我认为抽象电路板的维度是一个有趣的实验,并尝试使代码在任何电路板尺寸(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是好的,如果这样做的话:)

感谢您花时间阅读我的困境!

1 个答案:

答案 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始终是左参数。我的ZipperGrid2同构。

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