I'm trying to encode a type-level graph with some constraints on the construction of edges (via a typeclass) but I'm running into an "Illegal constraint in type" error when I try to alias a constructed graph. What's causing this issue? If it's unworkable, is there another way to encode the graph structure such that it can be built by type and folded over to yield a value-level representation of the graph?
Edit: Desiderata
I would like to be able to constrain the construction of a graph subject to the input
and output
nodes of any two operations.
For the sake of clarity, let's take the well-known case of length-indexed vectors.
An operation
would take an input
of some shape and potentially change it's length to the the length of output
. An edge between two operations
would need to ensure that the output of the first was compatible -- for some instance-defined notion of compatability -- with the input of the second. (Below, these constraints are omitted; the application requires dependently typed verification of the constraints and calculation of the types at compile.)
In order to define a new operation, S
, that can be used with the existing operation(s) T
(et al.), one should only need to add the data type S
, the implementation of S _
and the necessary constraints for the function of S
as an instance of the Edge
typeclass.
--Pragmas are needed additionally for the project in which this snippet is included
{-# LANGUAGE TypeInType, DataKinds, PolyKinds, ScopedTypeVariables,
FlexibleInstances, FlexibleContexts, GADTs, TypeFamilies,
RankNTypes, LambdaCase, TypeOperators, TemplateHaskell,
ConstraintKinds, PolyKinds, NoImplicitPrelude,
UndecidableInstances, MultiParamTypeClasses, GADTSyntax,
AllowAmbiguousTypes, InstanceSigs, DeriveFunctor,
FunctionalDependencies #-}
-- Algebra.Graph is from the algebraic-graphs package
import qualified Algebra.Graph as AG
import Data.Singletons
import Data.Singletons.Prelude
import Data.Singletons.TypeLits
import Data.Kind
data T (ln::Nat) c = T c
class Edge operation n o
instance
-- This would be something like: (LengthIsValidPrime x ~ True, y ~ DependentlyTypedCalculationForOpabc x) =>
Edge (T l c) x y
data Flow :: * -> * where
Empty :: Flow (a)
Vertex :: (Edge a n o) => a -> Flow (a)
Connect ::
(Edge a x y, Edge a y z, Edge a x z) =>
Flow (a) -> Flow (a) -> Flow (a)
Overlay ::
(Edge a x y, Edge a y z, Edge a x z) =>
Flow (a) -> Flow (a) -> Flow (a)
type Test c = Connect (Vertex (T 24 c )) (Vertex (T 3 c))
--which fails with
--error:
-- • Illegal constraint in a type: Edge a0 x0 z0
-- • In the type ‘Connect (Vertex (T 24 c)) (Vertex (T 3 c))’
-- In the type declaration for ‘Test’
-- We want to be able to define a graph like so:
type InputNode c = Vertex (T 100 c )
type ForkNode c = Vertex (T 10 c )
type NodeB c = Vertex (T 1 c )
type NodeC c = Vertex (T 1 c )
type PathA c = Connect (InputNode c) (ForkNode c)
type PathAB c = Connect (PathA c) (NodeB c)
type PathAC c = Connect (PathA c) (NodeC c)
type Output c = Vertex (T 2 c )
type Subgraph c = Overlay (Connect (PathAC c) (Output c)) (Connect (PathAB c) (Output c))
-- and eventually the trascription from the type-level graph to a value graph defined by Algebra.Graph
--foldFlow :: Flow a -> AG.Graph (Flow a)
--foldFlow Empty = AG.empty
--foldFlow vt@(Vertex x) = AG.vertex vt
--foldFlow (Overlay x y) = AG.overlay (foldFlow x) (foldFlow y)
--foldFlow (Connect x y) = AG.connect (foldFlow x) (foldFlow y)
--runGraph :: Subgraph c
--runGraph = ...create a term-level Subgraph c so we can fold over it.