In Haskell, how to bind one list-like monad to another list-like monad - haskell

Say you want to implement very general operations on a directed graph making as few assumptions about the structure as possible.
It is impossible to make absolutely no assumptions, so I am still assuming that I will represent my graph as some sort of adjacency list, but the spirit is to try to be as opaque as possible about the nature of manipulated things.
Assume you have the two following operations: one operation to list all nodes in a graph, and one operation to list all outgoing edges from some vertex.
class List_Nodes graph list vertex where
list_nodes :: graph -> list vertex
class List_Edges_From graph vertex list edge where
list_edges_from :: graph -> vertex -> list edge
Then, just for the fun of it I decided I might want want to iterate over all edges
class List_Edges graph vertex list edge where
list_edges :: graph -> list edge
No matter what the concrete implementation of a graph will be, I believe I can express very generally that listing edges can be understood as listing nodes, and listing edges from each of them.
So I decided to write an instance as general as possible like this:
instance (
Monad node_list,
Monad edge_list,
List_Nodes graph node_list vertex,
List_Edges_From graph vertex edge_list edge
) => List_Edges graph vertex edge_list edge where
list_edges graph = (list_nodes graph :: node_list vertex) >>= list_edges_from graph
-- I added :: node_list vertex to help GHC infer the type.
However, this code does not work as is. This code works only with an additional instance requirement that edge_list ~ node_list,. That's because binding happens only in one monad, the returned one: edge_list.
But to be as general as possible I do not want to assume that the way I store nodes, is necessarily the same way I store outgoing edges in a node. For example one might want to use a list to store nodes, and a vector to store edges out of a node.
Question:
How can I express the monadic bind list_nodes graph >>= list_edges_from graph between two possibly different list like containers?
More generally, how can I say convert a list to a vector without being specific about them? I am only assuming they are "list-like" whatever that means. Somehow these list like things are themselves functors, so I'm looking to convert some functor into some other functor. Am I looking for natural transformations of category theory? How can I do this in Haskell?
Language extensions used and imports used:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Lib () where
import Prelude
import Control.Monad

If you want to be very general about the monad in which your nodes and edges are stored, you can't really do anything. Two monads in general do not compose with each other: what should the return type be if nodes are "stored" as IO String and edges as String -> Maybe String?
I would suggest doing a lot less of this work at the type level. There is little need for type classes: instead, define a concrete type that contains the functions that you need, and a single typeclass for converting to that canonical type. Then the various implementations of your graph type can simply create a "canonical view" of their graph, representing it in the type that you use to implement generic algorithms. This way, you have only one canonical representation to perform these algorithms on, despite having many representations for the graphs themselves.
The graph type can be as simple as
data Graph v e = Graph { nodes :: [v]
, edges :: v -> [e]
}
class AsGraph t v e where
asGraph :: t v e -> Graph v e
and you can implement allEdges generically in terms of that quite easily. If you have a graph with vector edges, it can be converted to this generic graph type in order to participate in generic operations like allEdges:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Foldable (toList)
data Graph v e = Graph { nodes :: [v]
, edges :: v -> [e]
}
class AsGraph t v e where
asGraph :: t v e -> Graph v e
data VectorEdges v e = VectorEdges { vs :: [v]
, es :: v -> Vector e
}
instance AsGraph VectorEdges v e where
asGraph g = Graph (vs g) (toList . es g)
allEdges :: AsGraph t v e => t v e -> [e]
allEdges g = let g' = asGraph g
in nodes g' >>= edges g'

There does not seem to be something standard in Haskell to achieve my purpose, so I ended up adding a class specific for list conversion leaving me room to implement it for what I believe should be convertable lists.
class Convert_List list1 list2 element where
convert_list :: list1 element -> list2 element
Then I am free to implement it on my own.
The advantage of having such a class is that you can then write the graph operation like this:
class List_Nodes graph list vertex where
list_nodes :: graph -> list vertex
class List_Edges_From graph vertex list edge where
list_edges_from :: graph -> vertex -> list edge
class List_Edges graph vertex list edge where
list_edges :: graph -> list edge
instance (
Monad list,
List_Nodes graph l1 vertex,
List_Edges_From graph vertex l2 edge,
Convert_List l1 list vertex,
Convert_List l2 list edge
) => List_Edges graph vertex list edge where
list_edges graph =
convert_list (list_nodes graph :: l1 vertex) >>= \u ->
convert_list (list_edges_from graph u :: l2 edge)
Here you see that I implement list_edge in an very general way making few assumptions, i'm not even assuming the return list has to be the same as the graph internal representation.
This is also why I splitted each operation in its own class. Although this may seem counterintuitive at first I believe that there is more potential for factorization as shown here. If I had only one class containing the 3 operations, I could not implement only list_edges without enforcing constraints on the other operations as well.
It's only my opinion, but I believe more and more this sort of approach for code design has more potential for factoring.

Related

Data type for a simple, undirected graph without multiple edges or loops in Haskell

I am new to Haskell and I am trying to come up with a suitable way to represent a graph. First some background for an undirected simple graph. For all vertices u and v, an edge between u and v is the same as an edge between v and u, there is at most one edge between u and v, and there is no edge between u and u.
Later on I want to be able to write functions to check if the graph is 1) empty, 2) add new vertices, 3) add new edges, 4) obtain all neighbors to a vertex and 5) obtain a list of all vertices in a graph.
After doing research, I am a bit confused by all the ways to define a graph data type, which I also hope to get some help to clarify. All seem to agree that a you need some way to represent a Vertex/Node and the edges/links to other Vertices/Nodes. However, the implementations differ.
Before I have done a Tree with infinite amount of branches, following this question tree-with-an-arbitrary-number-of-branches
data Tree a = Node a [Tree a]
deriving (Eq, Show)
Whats different with a graph I guess is that a nodes on the same "level" and nodes on different "branches" can be connected with an edge, see figure below.
What I came up with first was defining a data type using recursive data structures with a type variable where each Vertex/Node has a list with its associated nodes:
data Node a = Node a [a]
deriving Show
data Graph a = Graph [Node a]
deriving Show
However, what I am a bit unsure about is whether this representation makes it possible to insert new edges later on. With this definition a graph is just a list of nodes that in turn contains list of nodes they link/edge to.
After doing research about how to represent a graph in Haskell I found some interesting ideas. The first was to define a graph just using type synonyms:
type Node = Int
type Element = String
type Edge = (Node, Node)
type Node = (Node, Element)
type Graph = ([Node], [Edge])
Here we have that a Graph is a list of nodes with an associated list of its connections/links/edges. However, I was not sure what the corresponding data type definition would look like and with a type variable/parameter instead of a concrete type. In this regard, I found this question declare-data-constructor-for-graphs suggesting to represent a graph like this:
type Vertex = Integer
data Graph = Graph [Vertex] [(Vertex, Vertex)]
Which I guess with type parameter instead could be translated to:
data Graph a = Graph [a] [(a, a)]
Is this correct? Would this solution work for creating a simple, undirected graph without multiple edges or loops in Haskell? That also support the creation of the specified functions above.
In continuation, similar to this representation, I found this question directed-acyclic-graph where a graph is defined as:
data Graph a = Graph [(a,[a])] -- Graph is a list of origins paired with edgeends
Here I guess the author defines a graph as a list of tuples where each tuple consists of one node and a list of its linked nodes.
Another way I found was to use record syntax in the question graph-data-type-representation.
data Node a = Node { value :: a
, neighbors :: [Node a]
} deriving (Show)
-- or simply,
data Node a = Node a [Node a]
deriving (Show)
Which I guess is the same reasoning. A Node/Vertex has a value, and neighbors that a just a list of other Vertices. Building on top of this, a graph definition would be:
data Graph a = Graph [Node a]
Or am I wrong? If so, this implementation is similar to what my initial thinking, but differs in the data Node definition. Not sure whats more correct here.
In summary, I have found many ways to represent a graph data type in Haskell. But I am a bit confused about which way that best suits my use-case, to create a simple, undirected graph without multiple edges or loops that also supports the functions I would like to implement.
Looking forward answers and comments to clarify this!
Ended up using
data Graph a = Graph [(a, [a])] deriving (Show)
Since Algebraic data types are just a way to represent data, it is mostly about choosing a design that fits your needs. Here is a good source to read more about it: Learn you a haskell
For example, choosing this data type representation makes adding vertices to a graph possible in this way.
addVertex :: Eq a => Graph a -> a -> Graph a
addVertex (Graph vList) v
| not (containsVertex v vList) = Graph (vList ++ makeVertex v)
| otherwise = Graph vList
makeVertex :: a -> [(a, [a])]
makeVertex x = [(x, [])]
containsVertex :: Eq a => a -> [(a, [a])] -> Bool
containsVertex _ [] = False
containsVertex x ((v, _) : ys) = x == v || containsVertex x ys
Thus, just easy to deal with and manage the Graph when expressed in this way.

Haskell: how to write code that interacts with the internals of two wrapped types?

I'm wondering how to create two encapsulated types that interact with each other, without exposing the internal implementation to other modules.
As an example, consider my two modules, Vector.hs and Matrix.hs, that wrap Linear.V4 (a 4-element vector) and Linear.M44 (a 4x4 element matrix). I wish to write a function that multiplies a Matrix by a Vector, returning a Vector, and using the wrapped data types to perform the operation.
In this example, in Vector.hs I have:
-- Vector.hs
module Vector (Vector, vector) where
import Linear (V4 (V4))
newtype Vector = Vector (V4 Double) deriving (Eq, Show, Read)
vector :: (Double, Double, Double, Double) -> Vector
vector (x, y, z, w) = Vector (V4 x y z w)
Note that I'm only exporting the new type and the factory function - the data constructor Vector is not exported. As far as I understand, this hides the internal implementation of Vector (i.e. that it's really a V4).
And in Matrix.hs I have something similar:
-- Matrix.hs
module Matrix (Matrix, vector)
import Linear (V4 (V4), M44)
type Row = (Double, Double, Double, Double)
newtype Matrix = Matrix (M44 Double) deriving (Eq, Show, Read)
matrix :: (Row, Row, Row, Row) -> Matrix
--matrix ((a00, a01, a02, a03), (a10, ... ) = Matrix (V4 (V4 a00 a01 a02 a03) (V4 a10 ...))
These two modules can be used by client code pretty effectively - the client code is not aware that they are implemented with Linear data structures, and there's apparently no way for client code to exploit that (is that true?).
The problem arises when those two types need to interact with each other at the level of their wrapped types - in this particular case, multiplying a Matrix by a Vector yields a new Vector. However to implement this operation, the code performing it needs access to the internal implementation of both Vector and Matrix, in order to do something like this:
-- Matrix.hs
-- ... include earlier definitions
{-# LANGUAGE FlexibleInstances #-}
import qualified Linear.Matrix ((!*))
class MatrixMultiplication a b c | a b -> c where
infixl 7 |*| -- set same precedence and associativity as *
(|*|) :: a -> b -> c
instance MatrixMultiplication Matrix Vector Vector where
(|*|) (Matrix a) q0 =
let (V4 x y z w) = a Linear.Matrix.!* _impl q0
in vector (x, y, z, w)
To do this I need a function, _impl, that allows the Matrix module to get at the internals of Vector:
-- Vector.hs
-- ... include earlier definitions
_impl :: Vector -> V4 Double
_impl (Vector q) = q
-- also added to export list
So after carefully hiding the internals of Vector (by not exporting the data constructor function of Vector), it seems I must expose them with a different exported function, and the only defence I have is some documentation telling client code they shouldn't be using it. This seems unfortunate - I've effectively just renamed the data constructor.
In a language like C++, the friend keyword could be used to provide a function access to the private data member of one of the types. Is there a similar concept in Haskell?
I suppose I could implement both Vector and Matrix in the same module. They'd then have access to each others' data constructors. However this isn't really a satisfying solution as it just works around the issue.
Is there a better approach?

Using subclass implementation in the definition of superclass functions

In my Haskell program I have some typeclasses representing abstract notions of "shapes", namely
-- | Class representing shapes.
class Shape a where
isColliding :: (Shape b) => a -> b -> Bool
centroid :: Point
-- | Class representing shapes composed of a finite number of vertices
and line segments connecting them.
class (Shape a) => Polygon a where
vertices :: a -> Vertices
As you can see, Polygon is naturally a subclass of Shape. I also have some data types that are instances of these different typeclasses. For example:
data Box = Box Point Point Angle
instance Shape Box where
...
instance Polygon Box where
...
---------------------------------
data Circle = Circle Point Radius
instance Shape Circle where
...
I have many more possible shapes, such as NGon, RegularNGon, etc. I would like to be able to implement isColliding, but the information required to calculate whether two shapes are colliding is dependent upon the implementation of the specific instance of Shape. For example, to calculate if two boxes are colliding, I need their list of vertices. So I have a few questions:
Is there anyway to "specialize" my function isColliding so that it is defined in a specific way for collisions of the type isColliding :: (Polygon b) => Box -> b -> Bool?
Is the structuring of my datatypes the best way to approach this problem, or am I misusing typeclasses and datatypes when the whole thing could be restructured to eliminate this problem?
I am rather new to Haskell, so if my question is worded poorly or any clarification is needed, please tell me.
Your current Shape class says “isColliding can tell whether this shape intersects another shape using only the methods of Shape on the other shape”, because its signature (Shape b) => a -> b -> Bool only tells you that b has an instance of Shape. So you’re right that this isn’t quite what you want.
One thing you can do is use MultiParamTypeClasses to describe a relationship between two types:
{-# LANGUAGE MultiParamTypeClasses #-}
class Colliding a b where
collidesWith :: a -> b -> Bool
And then make instances for various concrete combinations of types:
instance Colliding Circle Box where
Circle p r `collidesWith` Box p1 p2 θ = {- … -}
Here you know the concrete types of both a and b when defining the implementation. That might be good enough for your use case.
However, this leaves you with n2 instances if you have n types. And you’ll run into problems if you try to define polymorphic instances like this:
instance (HasBoundingBox b) => Colliding Circle b where
collidesWith = {- … -}
Because this overlaps with all your other instances for Colliding Circle: b will match any type, and only add the constraint that b must have an instance of HasBoundingBox. That constraint is checked after instance resolution. You can work around this with OverlappingInstances or the newer OVERLAPPABLE/OVERLAPPING/OVERLAPS pragmas to tell GHC to choose the most specific matching instance, but this might be more trouble than it’s worth if you’re just getting familiar with Haskell.
I’d have to think on it more, but there are definitely alternative approaches. In the simplest case, if you only need to deal with a few different kinds of shape, then you can just make them a single sum type instead of separate data types:
data Shape
= Circle Point Radius
| Box Point Point Angle
| …
Then your isColliding function can be of type Shape -> Shape -> Bool and just pattern-match on this type.
Generally speaking, if you’re writing a typeclass, it should come with laws for how instances should behave, like mappend x mempty == mappend mempty x == x from Data.Monoid. If you can’t think of any equations that should always hold for instances of your class, you should prefer to represent things with plain old functions and data types instead.

Can't match class type with specific type in haskell

I have the following situation in my code (simplified but semantically identical)
class Graph a where
edges :: EdgeSet c => a -> c
I have many subtypes that fulfill the graph interface. One of them is a tree
data Tree = Tree
instance Graph Tree where
edges tree = DirectedEdgeSet
A call on the edges method should return a DirectedEdgeSet. This should be OK since DirectedEdgeSet implements the EdgeSet class:
type Edge = (Int, Int)
data DirectedEdgeSet = DirectedEdgeSet (Set Edge) Int
class EdgeSet c where
content :: c -> Set Edge
size :: c -> Int
instance EdgeSet DirectedEdgeSet where
content (DirectedEdgeSet es _) = es
size (DirectedEdgeSet _ x) = x
This example does not compile:
• Couldn't match expected type ‘c’
with actual type ‘DirectedEdgeSet’
‘c’ is a rigid type variable bound by
the type signature for:
edges :: forall c. EdgeSet c => Tree -> c
at Tree.hs:10:5
• In the expression: DirectedEdgeSet
In an equation for ‘edges’: edges tree = DirectedEdgeSet
In the instance declaration for ‘Graph Tree’
• Relevant bindings include
edges :: Tree -> c (bound at Tree.hs:10:5)
Can someone tell me what I am doing wrong?
You are misunderstanding the type of the class method; edges :: EdgeSet c => a -> c is a function which takes any a (constrained with a being an instance of Graph) and returns any c (constrained with c being an instance of EdgeSet c). You probably instead want to say it returns some c obeying the aforementioned constraint.
Haskell 98 solution
You could just require that edges return an actual Set (like the one from Data.Set) for all graphs:
class Graph a where
edges :: a -> Data.Set Edge
ExistentialQuantification solution
Otherwise, you can use the ExistentialQuantification extension and modify the class method:
{-# LANGUAGE ExistentialQuantification #-}
data SomeEdgeSet = forall c. EdgeSet c => SomeEdgeSet c
class Graph a where
edges :: a -> SomeEdgeSet
instance Graph Tree where
edges tree = SomeEdgeSet DirectedEdgeSet
As you can tell, each one of your instances of Graph all return SomeEdgeSet when edges is used, but that SomeEdgeSet contains anything, as long as that thing is an instance of EdgeSet.
TypeFamilies solution
This is the solution I recommend. In general, for any Graph, you will only ever have one type of Edges it returns. Then, there is this cool feature with TypeFamilies where you can declare a type inside a class:
{-# LANGUAGE TypeFamilies, UndecideableInstances #-}
class (EdgeSet (Edges a)) => Graph a where
type Edges a
edges :: a -> Edges a
Then, suppose that your representation of edges for Graph Tree is DirectedEdgeSet, your instance will look like:
class Graph Tree where
type Edges Tree = DirectedEdgeSet -- `DirectedEdgeSet` is the type here
edges tree = DirectedEdgeSet -- `DirectedEdgeSet` is the constructor here
In edges, the type variable c is universally quantified. That means that edges must work for all types which are instances of EdgeSet. Your implementation fixes a concrete type and thus does not work for all EdgeSet instances.
To fix this, you can specify the concrete type of edge set that the instance uses via type families:
{-# LANGUAGE TypeFamilies #-}
data Tree = Tree
data DirectedEdgeSet = DirectedEdgeSet
class Graph a where
type GraphEdgeSet a :: *
edges :: a -> GraphEdgeSet a
instance Graph Tree where
type GraphEdgeSet Tree = DirectedEdgeSet
edges tree = DirectedEdgeSet

How to require typeclass instance in data definition

Assume I have a type like this:
data Graph vertex = Graph {
vertices :: [vertex],
edgelist :: [(vertex, [vertex])]
}
But I would like to have a typeclass constraint on the type variable vertex, I was attempting to do it like it's done with typeclass definitions:
data (Eq vertex) => Graph vertex = Graph {
vertices :: [vertex],
edgelist :: [(vertex, [vertex])]
}
But that produces a syntax error Illegal datatype context (use DatatypeContexts). What is the correct way to achieve this? Or is it not possible?
The Haskell convention is to never put constraints on data types. Instead, put them on the functions operating on the data types. This allows you to put the constraints only on those functions that actually need them.
One thing you can do is to use GADTs:
{-# language GADTs #-}
data Graph vertex where
Graph :: (Eq vertex) => { vertices :: [vertex], edgelist :: [(vertex, [vertex])] } -> Graph vertex
this will ensure that you will always have Eq vertex in scope when you pattern match on the Graph constructor.

Resources