How to require typeclass instance in data definition - haskell

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.

Related

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

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.

Is there a way to inform Haskell to unwrap a type class inside a sum type

I will briefly explain my chain of thought before the example so that if any of it does not make sense we are able to fix that as well.
Suppose that for each type constructor of my data declaration (a sum of product types) there is one parameter that has an instance to a given type class. In my head, that means that I would be able to explain to GHC/Haskell how to get to that specific type so that my type would end up behaving like an instance of that type class.
For example:
data Vector
-- The class type I talked about
class Transformable a where
setPosition' :: Vector -> a -> IO ()
setOrigin' :: Vector -> a -> IO ()
setAngle' :: Float -> a -> IO ()
-- ... this goes a long way
data TCircleShape
data TSquareShape
data TTriangleShape
data TConvexShape
-- Large sum type that defines different types of Shape
data Shape = Circle Float TCircleShape
| Square Float String TSquareShape
| Triangle Float TTriangleShape
| Convex [Vector] Float TConvexShape
-- ...
-- Almost all of the the Shape constructors have at least one
-- parameter that has an instance of the Transformable typeclass:
instance Transformable TCircleShape
instance Transformable TSquareShape
instance Transformable TTriangleShape
instance Transformable TConvexShape
-- What I would like to write then is:
runOnTransformable :: Transformable a => (a -> IO ()) -> Shape -> IO ()
runOnTransformable = undefined -- (???)
-- What I am doing right now is simply expanding Shape manually:
setShapePosition :: Vector -> Shape -> IO ()
setShapePosition v (Circle _ ptr) = setPosition' v ptr
setShapePosition v (Square _ _ ptr) = setPosition' v ptr
-- and so on...
setShapeAngle' :: Float -> Shape -> IO ()
setShapeAngle' f (Circle _ ptr) = setAngle' f ptr
setShapeAngle' f (Convex _ _ ptr) = setAngle' f ptr
-- and so on...
There is a clear pattern in my eyes and I would like to have some way of abstracting this unwrapping somehow.
One might try to have an instance for the data type itself:
instance Transformable Shape where
setPosition' v (Circle _ ptr) = setPosition' v ptr
-- [...]
setAngle' f (Convex _ _ ptr) = setAngle' f ptr
-- [...]
The downside is that I would have to 'reimplement' all the methods by manually unwrapping the type classes again, except it is in an instance declaration. Right?
Coming back to my question: Is there a way to inform Haskell how to unwrap and act upon a type class from a sum type?
I have a really thin familiarity with Lens and none with TemplateHaskell, however, if using said features would be a probable solution, by all means, go for it.
Your runOnTransformable function is not possible to write as specified, because its type signature is wrong.
runOnTransformable :: Transformable a => (a -> IO ()) -> Shape -> IO ()
means that for any a, which the caller of runOnTransformable chooses, they can provide you a function taking that specific a, and you will call that function with an a of the right type, which you will produce somehow from the Shape object you have. Now, that is clearly not possible, because they may pass you a function of type TSquareShape -> IO () but a Shape which has no TSquareShape in it. Worse, GHC will worry that someone may define instance Transformable Integer where {...}, and you need to be able to handle that case too even though your Shape type doesn't have any way to guess what Integer to give to this function.
You don't want to say that your function works for any Transformable a => a, but rather that the caller's function must work for any Transformable a => a, so that it will be willing to accept whatever value happens to live in your Shape type. You will need the RankNTypes extension to enable you to write the correct signature:
runOnTransformable :: (forall a. Transformable a => a -> IO ()) -> Shape -> IO ()
Sadly, after you've done this I still don't know an automated way to implement this function for all of the various constructors of Shape. I think something ought to be possible with Generic or Data, or Template Haskell or something, but that's beyond my knowledge. Hopefully what I've written here is enough to get you moving in the right direction.
Warning: Speculative answer, proceed with care.
Here is an alternative approach that uses data families. A data family is, in essence, a type-level function which introduces brand new types for their results. In this case, the data families ShapeData and TShape are used to produce the types of the Shape fields.
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
data Circle'
data Square'
data family ShapeData s
newtype instance ShapeData Circle' = DCircle Float
deriving (Eq, Show)
data instance ShapeData Square' = DSquare Float String
deriving (Eq, Show)
data family TShape s
data instance TShape Circle' = TCircle
data instance TShape Square' = TSquare
class Transformable a where
setAngle' :: Float -> a -> IO ()
instance Transformable (TShape Circle') where
setAngle' _ _ = putStrLn ("Setting a circle angle is a no-op")
instance Transformable (TShape Square') where
setAngle' x _ = putStrLn ("Setting the square angle to " ++ show x)
data Shape a = Shape (ShapeData a) (TShape a)
instance Transformable (TShape a) => Transformable (Shape a) where
setAngle' x (Shape _ t) = setAngle' x t
Additional remarks:
In addition to data families, there are also type families, which result in preexisting types rather than newly introduced ones. Since here we would have to define TCircleShape, TSquareShape etc. separately, we might as well do that through a data family.
I replaced your Shape constructors with empty data types, which are then used to fill in the gaps of a now parametric Shape type. One significant difference in relation to your sum type approach is that the set of possible shapes is now open to extension. If you needed it to be closed, I believe it would be possible by reaching to something even fancier: singletons -- you would define a sum type data Shape' = Circle' | Square', then use e.g. the singletons machinery to promote the constructors to type level and using the resulting types as parameters to Shape.

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

Ambiguous type variable 'blah' in the constraint... how to fix?

I'm trying to write a simple ray-tracer in Haskell. I wanted to define a typeclass representing the various kinds of surfaces available, with a function to determine where a ray intersects them:
{-# LANGUAGE RankNTypes #-}
data Vector = Vector Double Double Double
data Ray = Ray Vector Vector
class Surface s where
intersections :: s -> Ray -> [Vector]
-- Obviously there would be some concrete surface implementations here...
data Renderable = Renderable
{ surface :: (Surface s) => s
, otherStuff :: Int
}
getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra
However this gives me the error:
Ambiguous type variable 's' in the constraint:
'Surface'
arising from a use of 'surface'
(The actual code is more complex but I've tried to distill it to something simpler, while keeping the gist of what I'm trying to achieve).
How do I fix this? Or alternatively, given that I come from a standard OO background, what am I fundamentally doing wrong?
Please don't use existential types for this! You could, but there would be no point.
From a functional standpoint you can drop this typeclass notion of Surface entirely. A Surface is something that maps a Ray to a list of Vectors, no? So:
type Surface = Ray -> [Vector]
data Renderable = Renderable
{ surface :: Surface
, otherStuff :: Int
}
Now if you really want, you can have a ToSurface typeclass essentially as you gave:
class ToSurface a where
toSurface :: a -> Surface
But that's just for convenience and ad-hoc polymorphism. Nothing in your model requires it.
In general, there are a very few use cases for existentials, but at least 90% of the time you can substitute an existential with the functions it represents and obtain something cleaner and easier to reason about.
Also, even though it may be a tad too much for you to take in, and the issues don't exactly match, you might find useful some of Conal's writing on denotational design: http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/
In your getRenderableIntersections function you call surface. There is no way for the interpreter to figure out what instance of the class Surface you want to use. If you have two such instances:
instance Surface SurfaceA where
-- ...
instance Surface SurfaceB where
-- ...
How can the interpreter determine the type of surface?
The way you defined Renderable means there is a function surface :: Surface s => Renderable -> s.
Try creating an instance Surface SurfaceA and asking the following type query (given a simple constructor SurfaceA):
> :t surface (Renderable SurfaceA 0) -- What's the type of the expression?
So, what type is this expression? I bet you're expecting SurfaceA. Wrong. Take the type of surface. It takes a Renderable argument and we're passing it a Renderable argument. What is left after that? Surface s => s. That's the type of that expression. We still don't know what type does s represent.
If you want the type to be SurfaceA you need to change your code so it becomes something like surface :: Surface s => Renderable s -> s. This way what s is can be determined, because it is the same s used in Renderable.
EDIT: As suggested by #mokus, you could also try the ExistentialTypes extension. It allows "hiding" away type parameters on the right side of a type declaration.
data Renderable = forall s. Surface s => Renderable
{ surface :: s
, otherStuff :: Int
}
The HaskellWiki page I linked to above even has an example very similar to what you want to do.
EDIT: (By #stusmith) - For the record, I'm including code below which compiles based on these suggestions here. However I've accepted the answer which I think shows a better way of approaching things.
{-# LANGUAGE ExistentialQuantification #-}
data Vector = Vector Double Double Double
data Ray = Ray Vector Vector
class Surface_ s where
intersections :: s -> Ray -> [Vector]
data Surface = forall s. Surface_ s => Surface s
instance Surface_ Surface where
intersections (Surface s) ra = intersections s ra
data Renderable = Renderable
{ surface :: Surface
}
getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra

Haskell Ord instance with a Set

I have some code that I would like to use to append an edge to a Node data structure:
import Data.Set (Set)
import qualified Data.Set as Set
data Node = Vertex String (Set Node)
deriving Show
addEdge :: Node -> Node -> Node
addEdge (Vertex name neighbors) destination
| Set.null neighbors = Vertex name (Set.singleton destination)
| otherwise = Vertex name (Set.insert destination neighbors)
However when I try to compile I get this error:
No instance for (Ord Node)
arising from a use of `Set.insert'
As far as I can tell, Set.insert expects nothing but a value and a set to insert it into. What is this Ord?
In GHCi:
> import Data.Set
> :t insert
insert :: (Ord a) => a -> Set a -> Set a
So yes, it does expect Ord. As for what Ord means, it's a type class for ordered values. It's required in this case because Data.Set uses a search tree, and so needs to be able to compare values to see which is larger or if they're equal.
Nearly all of the standard built-in data types are instances of Ord, as well as things like lists, tuples, Maybe, etc. being instances of Ord when their type parameter(s) are. The most notable exception, of course, are functions, where no sensible concept of ordering (or even equality) can be defined.
In many cases, you can automatically create instances of type classes for your own data types using a deriving clause after the declaration:
data Foo a = Foo a a Int deriving (Eq, Ord, Show, Read)
For parameterized types, the automatic derivation depends on the type parameter also being an instance, as is the case with lists, tuples, and such.
Besides Ord, some important type classes are Eq (equality comparisons, but not less/greater than), Enum (types you can enumerate values of, such as counting Integers), and Read/Show (simple serialization/deserialization with strings). To learn more about type classes, try this chapter in Real World Haskell or, for a more general overview, there's a Wikipedia article.
Haskell sets are based on a search tree. In order to put an element in a search tree an ordering over the elements must be given. You can derive Ord just like you are deriving Show by adding it to your data declaration, i.e.:
data Node = Vertex String (Set Node)
deriving (Show, Eq, Ord)
You can see the requirement of Ord by the signature of Data.Set.insert
(Ord a) => a -> Set a -> Set a
The part (Ord a) => establishes a constraint that there is an instance of the typeclass Ord for a. The section on type classes in the haskell tutorial gives a more thorough explanation.

Resources