如何在haskell中对我称之为“异构图”的模型进行建模,以便在编译时验证图的类型正确性?
为此,异构图是一组节点,每个节点都有一个特定类型的标签,还有一组边,每个边都有一个源类型标签和一个目标类型标签。
我们希望静态确保在将边添加到图中时,该边的源类型标签与源节点的类型标签以及该边的目标类型标签匹配匹配目标节点的类型标签。但我们不希望以微不足道的方式做到这一点(通过强制整个图形仅包含具有一个特定类型标签的节点)。
答案 0 :(得分:1)
我不确定如何在编译时强制执行此操作 - 我认为它要求您的图形完全是静态的? - 但是在运行时使用SELECT *
FROM yourtable t
CROSS APPLY STRING_SPLIT (t.subproductid , ',')
执行它是相对简单的。这是一个看起来像什么的草图。首先,我将从键入的Typeable
和Node
类型开始:
Edge
将它们包裹在存在之中:
data Node a = Node a
data Edge a b = Edge !Int !Int
拥有使用存在量化类型的异构图数据类型:
{-# LANGUAGE ExistentialQuantification #-}
import Data.Typeable
data SomeNode
= forall a. (Typeable a)
=> SomeNode (Node a)
data SomeEdge
= forall a b. (Typeable a, Typeable b)
=> SomeEdge (Edge a b)
然后执行动态类型检查的操作:
import Data.IntMap (IntMap)
-- Not a great representation, but simple for illustration.
data Graph = Graph !(IntMap SomeNode) [SomeEdge]
现在成功了:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
import qualified Data.IntMap as IntMap
addNode
:: forall a. (Typeable a)
=> Int -> a -> Graph -> Maybe Graph
addNode i x (Graph ns es) = case IntMap.lookup i ns of
-- If a node already exists at a given index:
Just (SomeNode (existing :: Node e)) -> case eqT @e @a of
-- Type-preserving replacement is allowed, but…
Just Refl -> Just $ Graph ns' es
-- …*type-changing* replacement is *not* allowed,
-- since it could invalidate existing edges.
Nothing -> Nothing
-- Insertion is of course allowed.
Nothing -> Just $ Graph ns' es
where
ns' = IntMap.insert i (SomeNode (Node x)) ns
-- To add an edge:
addEdge
:: forall a b. (Typeable a, Typeable b)
=> Edge a b -> Graph -> Maybe Graph
addEdge e@(Edge f t) (Graph ns es) = do
-- The ‘from’ node must exist…
SomeNode (fn :: Node tfn) <- IntMap.lookup f ns
-- …and have the correct type; and
Refl <- eqT @a @tfn
-- The ‘to’ node must exist…
SomeNode (tn :: Node ttn) <- IntMap.lookup t ns
-- …and have the correct type.
Refl <- eqT @b @ttn
pure $ Graph ns $ SomeEdge e : es
但是,将pure (Graph mempty mempty)
>>= addNode 0 (1 :: Int)
>>= addNode 1 ('x' :: Char)
>>= addEdge (Edge 0 1 :: Edge Int Char)
中的Int
/ Char
更改为无效类型,或将Edge Int Char
/ 0
更改为无效索引,将会失败并返回{{1} }。