Can't match class type with specific type in haskell - 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

Related

Is it possible to promote a value to type level?

Doing this just for fun but I don't end up figuring this out.
Say I have a typeclass that unifies coordinate system on squares and hexagons:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Proxy
data Shape = Square | Hexagon
class CoordSystem (k :: Shape) where
type Coord k
-- all coordinates arranged in a 2D list
-- given a side length of the shape
allCoords :: forall p. p k -> Int -> [[Coord k]]
-- neighborhoods of a coordinate.
neighborsOf :: forall p. p k -> Int -> Coord k -> [Coord k]
-- omitting implementations
instance CoordSystem 'Square
instance CoordSystem 'Hexagon
Now suppose I want to use this interface with a s :: Shape that is only known at runtime. But to make use of this interface, at some point I'll need a function like this:
-- none of those two works:
-- promote :: CoordSystem k => Shape -> Proxy (k :: Shape) -- signature 1
-- promote :: Shape -> forall k. CoordSystem k => Proxy (k :: Shape)
promote s = case s of
Square -> Proxy #'Square
Hexagon -> Proxy #'Hexagon
However this does not work, if signature 1 is uncommented:
• Couldn't match type ‘k’ with ‘'Square’
‘k’ is a rigid type variable bound by
the type signature for:
promote :: forall (k :: Shape). CoordSystem k => Shape -> Proxy k
at SO.hs:28:1-55
Expected type: Proxy k
Actual type: Proxy 'Square
Understandably, none of 'Square, 'Hexagon, k :: Shape unifies with others, so I have no idea whether this is possible.
I also feel type erasure shouldn't be an issue here as alternatives of Shape can use to uniquely identify the instance - for such reason I feel singletons could be of use but I'm not familiar with that package to produce any working example either.
The usual way is to use either an existential type or its Church encoding. The encoded version is actually easier to understand at first, I think, and closer to what you already attempted. The problem with your forall k. CoordSystem k => {- ... thing mentioning k -} is that it promises to polymorph into whatever k the user likes (so long as the user likes CoordSystems!). To fix it, you can demand that the user polymorph into whatever k you like.
-- `a` must not mention `k`, since `k` is not
-- in scope in the final return type
promote :: forall a. Shape -> (forall k. CoordSystem k => Tagged k a) -> a
promote Square a = unTagged (a #Square)
promote Hexagon a = unTagged (a #Hexagon)
-- usage example
test = promote Hexagon (unproxy $ \p -> length (allCoords p 15))
Note that on the right hand side of the = sign, a has the type forall k. CoordSystem k => {- ... -} that says the user gets to choose k, but this time you're the user.
Another common option is to use an existential:
data SomeSystem where
-- Proxy to be able to name the wrapped type when matching on a SomeSystem;
-- in some future version of GHC we may be able to name it via pattern-matching
-- on a type application instead, which would be better
SomeSystem :: CoordSystem k => Proxy k -> SomeSystem
Then you would write something like
promote :: Shape -> SomeSystem
promote Square = SomeSystem (Proxy #Square)
promote Hexagon = SomeSystem (Proxy #Hexagon)
-- usage example
test = case promote Hexagon of SomeSystem p -> length (allCoords p 15)
and then the user would pattern match to extract the CoordSystem instance from it.
A final choice is singletons:
data ShapeS k where
SquareS :: ShapeS Square
HexagonS :: ShapeS Hexagon
Here we have made a direct connection between SquareS at the computation level and Square at the type level (resp. HexagonS and Hexagon). Then you can write:
-- N.B. not a rank-2 type, and in particular `a` is
-- now allowed to mention `k`
promote :: ShapeS k -> (CoordSystem k => a) -> a
promote SquareS a = a
promote HexagonS a = a
The singletons package offers tools for automatically deriving the singleton types that correspond to your ADTs.

How to statically check a graph validity?

Consider the code below.
newtype NodeAT = NodeAT String deriving (Show,Read,Eq,Ord)
newtype NodeBT = NodeBT String deriving (Show,Read,Eq,Ord)
newtype NodeCT = NodeCT String deriving (Show,Read,Eq,Ord)
newtype NodeDT = NodeDT String deriving (Show,Read,Eq,Ord)
nodeA = NodeAT "nodeA"
nodeB = NodeBT "nodeB"
nodeC = NodeCT "nodeC"
nodeD = NodeDT "nodeD"
data Graph n m = Graph
{ vertices :: n
, edges :: m
}
graph1 = Graph (nodeA,nodeB,nodeC,nodeD) ((nodeA,nodeC),(nodeB,nodeC),(nodeA, nodeC))
Is there any possibility to use the type system to check that edges pairs are instances of nodes belonging to vertices tuple ?
This would make by construction
graph2 = Graph (nodeA, nodeB) (nodeA, nodeC)
illegal and failing at compile time ?
Not sure if it fits your needs, but you can come a long way with a bit of type-level programming.
Some extensions are needed of course:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
Here is a closed type family for testing whether a type is a member of a type-level list of types:
type family Elem a as :: Bool where
Elem a '[] = False
Elem a (a : as) = True
Elem a (b : as) = Elem a as
Now, let's define types of vertices and edges where vertices are drawn from a given list of vertex types:
data Vertex :: [*] -> * where
V :: Elem a as ~ True => a -> Vertex as
type Edge as = (Vertex as, Vertex as)
Then, we can define a type of graphs where the vertices are stored in its type and its edges in a data constructor:
data Graph :: [*] -> * where
G :: [Edge as] -> Graph as
Here are some vertex types:
data NodeA = NodeA
data NodeB = NodeB
data NodeC = NodeC
With that, the following graph is well-typed:
graph1 :: Graph [NodeA, NodeB]
graph1 = G [(V NodeA, V NodeB)]
But the following is not:
graph2 :: Graph [NodeA, NodeB]
graph2 = G [(V NodeA, V NodeC)]
It fails with:
error:
• Couldn't match type ‘'False’ with ‘'True’
arising from a use of ‘V’
• In the expression: V NodeC
In the expression: (V NodeA, V NodeC)
In the first argument of ‘G’, namely ‘[(V NodeA, V NodeC)]’

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.

Creating Haskell datatype accepting type of non-* kind in one of its constructors

Hello. I am playing with Ivory library which relies heavily on modern features of Haskell. Among others, it defines the typeclasses IvoryType accepting all types and IvoryArea accepting types of special kind Area. The definitions look like this:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExistentialQuantification #-}
-- | Proxy datatype with a phantom arbitrary-kinded type
-- and a single constructor
data Proxy (a :: k) = Proxy
-- | The kind of memory-area types.
data Area k
= Struct Symbol
| Array Nat (Area k)
| CArray (Area k)
| Stored k
-- ^ This is lifting for a *-kinded type
class IvoryType t where
ivoryType :: Proxy t -> I.Type {- arguments are not important -}
-- | Guard the inhabitants of the Area type, as not all *s are Ivory *s.
class IvoryArea (a :: Area *) where
ivoryArea :: Proxy a -> I.Type {- arguments are not important -}
OK. Now let's try to express the fact that we are going to store values with ivoryType function defined. Obviously, they are the memebers of IvoryType class, so the answer is
data TypeStorage = TypeStorage (forall t . IvoryType t => t)
So far so good. Now we want to store values which have ivoryArea function defined. Let's use the IvoryArea class as a filter condition, like in the prevoius case:
data AreaStorage = AreaStorage (forall t . IvoryArea t => t)
Surprisingly, the compiler (ghc version 7.8.4) outputs an error
src/IvoryLL/Types.hs:59:45:
Expected a type, but ‘t’ has kind ‘Area *’
In the type ‘forall t. IvoryArea t => t’
In the definition of data constructor ‘AreaBase’
In the data declaration for ‘Area
Could you please explain, how to express the ownership of ivoryArea function in Haskell properly ?
Edit
Some links to the original declarations:
https://github.com/GaloisInc/ivory/blob/master/ivory/src/Ivory/Language/Type.hs
https://github.com/GaloisInc/ivory/blob/master/ivory/src/Ivory/Language/Area.hs
Now that we've established in the comments that you can't do what you want directly, which is create a special "subkind" of all types, we can use a bit more legwork to get what you want.
We just use a (closed) type family to interpret your Area * kind into something of kind * and then GADT, indexed by Area *, to hold such values. We can then wrap the whole shebang up in an existential to store arbitrary values of such a kind, if so desired.
Consider this cut down example:
data Area k
= Stored k
| List (Area k)
type family InterpIvoryArea a :: * where
InterpIvoryArea (Stored k) = k
InterpIvoryArea (List a) = [InterpIvoryArea a]
data AreaStorage a where
AreaStorage :: InterpIvoryArea a -> AreaStorage a
data AreaStorageExistential where
AreaStorageExistential :: AreaStorage a -> AreaStorageExistential
testValue :: AreaStorageExistential
testValue = AreaStorageExistential (AreaStorage [1,2,3] :: AreaStorage (List (Stored Int)))

Defining a Torus class

I would like to define a Torus class representing 2D arrays that wrap around at borders. For example, accessing the array with an index beyond the size of the corresponding dimension should return the element at position i mod arraysize. My class should thus only need to define a get method as such:
class Torus a where
get :: a -> Int -> Int -> b
This definition is imprecise. a is supposed to be a 2D array and b should be the type contained in the array. How can I express this?
Thanks,
I suppose you were only looking for the types in the class:
class Torus t where
get :: t a -> Int -> Int -> a
Or did you look for an appropriate instance?
You can also use functional dependencies or type families in case you want to have something more sophisticated than simple t :: * -> * type:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Torus t v | t -> v where
get :: t -> Int -> Int -> v
instance Torus (Vector a) a where
get = ...
or
{-# LANGUAGE TypeFamilies #-}
class Torus t where
type Element t
get :: t -> Int -> Int -> Element t
instance Torus (Vector a) where
type Element (Vector a) = a
get = ...
This requires some GHC extensions though.

Resources