Haskell 如何为“索引”;“元素”;按a";源容器“;价值

Haskell 如何为“索引”;“元素”;按a";源容器“;价值,haskell,dependent-type,Haskell,Dependent Type,因此,我有一个非常类似于此(非常简化)代码的情况: 所有这些代码都要求您永远不要“混合”来自不同容器的元素。您可以通过将类型与每个容器关联来静态区分容器。容器元素用类型标记,以确定两个给定元素是否来自同一容器。这使用了-XExistentialQuantification和-XRank2Types。基本上,这是依赖类型,只是类型依赖于标记而不是容器值 -- Containers with existentially typed tags 'c' data Container a = forall

因此,我有一个非常类似于此(非常简化)代码的情况:


所有这些代码都要求您永远不要“混合”来自不同容器的
元素。

您可以通过将类型与每个容器关联来静态区分容器。容器元素用类型标记,以确定两个给定元素是否来自同一容器。这使用了
-XExistentialQuantification
-XRank2Types
。基本上,这是依赖类型,只是类型依赖于标记而不是容器值

-- Containers with existentially typed tags 'c'
data Container a = forall c. Container !(OpenContainer c a)

-- Containers with a tag parameter 'c'
data OpenContainer c a = OpenContainer [a]

-- A container element with a tag parameter 'c'
data Element c a = Element (OpenContainer c a) a

-- Create a container.  An arbitrary tag is chosen for the container.
container :: [a] -> Container a
container = Container . OpenContainer

-- Use a container.  The tag is read.
openContainer :: Container a -> (forall c. OpenContainer c a -> b) -> b
openContainer c k = case c of Container c' -> k c'

-- Get a container's elements.  The elements are tagged.
getElements :: OpenContainer c a -> [Element c a]
getElements c@(OpenContainer xs) = map (Element c) xs
无论何时调用
openContainer
,它都将生成属于同一容器的值的集合。将假定对
openContainer
的两个不同调用引用不同的容器

-- Ok
f c = openContainer c $ \oc -> getElements oc !! 0 `before` getElements oc !! 1

-- Error
f c d = openContainer c $ \oc -> openContainer d $ \od -> getElements oc !! 0 `before` getElements od !! 0
这是安全的,但比较保守,因为它不是基于使用了哪个容器,而是基于使用了哪个调用
openContainer
。。在容器上调用
openContainer
,然后再次调用,将产生不兼容的元素

-- Error
f c = openContainer c $ \oc -> openContainer c $ \od -> getElements oc !! 0 `before` getElements od !! 1
现在,您可以在
之前编写
,而无需显式测试是否相等。由于两个元素具有相同的索引,因此它们必须来自同一个容器

before :: Eq a => Element c a -> Element c a -> Bool
before (Element (OpenContainer xs) x) (Element _ y) = fromJust (elemIndex x xs) < fromJust (elemIndex y xs)
before::Eq a=>元素ca->元素ca->Bool
在(Element(OpenContainer xs)x之前(Element_y)=fromJust(elemIndex x xs)
我不知道这是否可以作为一个答案,但我会把它扔出去。您可以使每个元素成为其派生的容器的函数:

newtype a `HasA` b = H { using :: a -> b }
    deriving (Monad, Applicative, Functor)
通过将允许的操作集限制为上述类,可以确保组合的任何元素共享同一原始容器

before :: Eq a => Element c a -> Element c a -> Bool
before (Element (OpenContainer xs) x) (Element _ y) = fromJust (elemIndex x xs) < fromJust (elemIndex y xs)
导出“using”函数,但不导出H构造函数。创建元素的基本函数由您提供,但是用户可以使用Monad实例将它们组合在一起,确保在您展开并提供容器后,它们始终引用同一容器

before :: Eq a => Element c a -> Element c a -> Bool
before (Element (OpenContainer xs) x) (Element _ y) = fromJust (elemIndex x xs) < fromJust (elemIndex y xs)

作为奖励,它从字面上回答了标题的问题:它根据其容器的值对元素进行索引。

这似乎与是否可以检测两个值是否正好相等或甚至相同有关。实际上,我刚才正在尝试一个类似的解决方案,使用
RankNTypes
并且没有存在,但是我没有想到我已经合并了
OpenContainer
中间类型;
Ix(Element ca)
实例似乎也可以正常工作。然而,说实话,我有点希望有人能想出一些不需要CPS的东西,因为代码变得毛茸茸的。(或者这是我学习
ContT
?)您不需要CPS转换您的一元代码。只需编写
openContainer c$\x->do{…}
。它会改变压痕,但不会改变monad管道。我认为这个建议适用于类似的情况,但并不完全相同。如果我没有弄错,您的解决方案要求创建
元素
时不要使用
容器
,这将在以后使用
元素
时提供。这不适用于我的案例,但有趣的是,我只是在我的项目中遇到了一些与这个想法密切相关的东西。更有趣的是,它与散热器的答案一起应用-
newType SharedContainer s a=SharedContainer(OpenContainer s a->OpenThings s a)
证明有非常有用的
Functor
Applicative
实例。。。