It's my first exercise to understand the classes in Haskell. My problem is how to define the functions that I have declared in the class and how to test them by terminal ghci.
I explain step by step what I did:
type Point2d = (Int, Int) -- point
type Vector2d = (Int, Int) -- vector
data Shape =
Line Point2d Point2d
| Triangle Point2d Point2d Point2d
deriving (Eq, Show)
class ClassShape s where
name :: s -> String
perim :: s -> Int -- given a CShape calculates the perimeter
move :: s -> Vector2d -> s
Now, I declare s as ClassShape instance, by implementing the corresponding functions.
nameShape :: Shape s -> String
nameShape Line = "Line"
nameShape Triangle = "Triangle"
perimShape :: Shape s -> Int
perimShape Line a b = 999 -- ...
perimShape Triangle a b c = 999 -- ...
Here's my problem: how should I declare the functions? I just need to see an "example" to understand the concept.
The error that Haskell returns is:
`Shape' is applied to too many type arguments
In the type signature for `nameShape':
nameShape :: Shape s -> String
`Shape' is applied to too many type arguments
In the type signature for `perimShape':
perimShape :: Shape s -> Int
Then, how do I test the program on Haskell?
Thanks to all.
Note that nameShape function will not work, because there is no Shape s type defined. Remember that s is a type variable. Only if you have defined Shape s type constructor you can use them. You have defined Shape type in your definition but not Shape s. For defining instance of typeclass, you have to do something like this:
instance ClassShape Shape where
name (Line _ _) = "Line"
name (Triangle _ _ _) = "Triangle"
perim (Line x y) = undefined -- Calculate perimiter using x and y
perim (Triangle x y z) = undefined
move (Line x y) = undefined
move (Triangle x y z) = undefined
You have to fill the undefined with working parts.
You're making a common confusion of early Haskell programmers: using two different things which work in related ways (sum types and classes) to do the same thing in two different ways. Thus there are two problems: the "little" problem (what does this error mean?) and the "big" problem (why is your code shaped like this?).
The Little Problem
You wrote Shape s when you meant to just write Shape. The way you have defined Shape, it has kind * (that is, it is a concrete type) rather than kind * -> *, which is the kind of adjectives -- things like "a list of" or "a pair of" which are abstract until you give them a concrete type to modify ("a list of strings" is concrete, "a list of" is abstract). When you write Shape s you are applying Shape as an adjective to a type variable s but it's not an adjective; it's a noun.
That is why you get the error:
`Shape' is applied to too many type arguments
Side note: you may be used to languages where the error message usually is not very well-related to the actual problem. In Haskell usually the compiler tells you exactly what is wrong, as it did in this case.
The Big Problem
Type classes are collections of unrelated types which can do the same things. The type class syntax passes an implicit context as a "constraint", this context can be implicit because it belongs to the type.
You may need to read that last paragraph a few times in a quiet corner. Basically I mean to say that you can do the same thing as a type class with a data constructor for the context:
data EqOrd s = EqOrdLib {getEq :: s -> s -> Bool, getCmp :: s -> s -> Ordering}
-- this is just provided to us as a primitive by Haskell
intEOL :: EqOrd Int
intEOL = EqOrdLib (==) compare
-- but we can then define things like this:
listEOL :: EqOrd x -> EqOrd [x]
listEOL (EqOrdLib base_eq base_cmp) = EqOrdLib list_eq list_cmp where
list_cmp [] [] = EQ
list_cmp (_:_) [] = GT
list_cmp [] (_:_) = LT
list_cmp (x:xs) (y:ys) = case base_cmp x y of
LT -> LT
GT -> GT
EQ -> list_cmp xs ys
list_eq xs ys = list_cmp xs ys == EQ
Now to use that sort of context, you would have to write explicitly:
quicksort :: EqOrd x -> [x] -> [x]
quicksort _ [] = []
quicksort lib (p:els) = quicksort lib lesser ++ [p] ++ quicksort lib greater
where cmp = getCmp lib
p_less_than x = cmp x p == LT
p_gte x = not . p_less_than
greater = filter p_less_than els
lesser = filter p_gte els
See, we explicitly pass in this library of functions lib and explicitly pull out the comparison function cmp = getCmp lib.
Type classes allow us to implicitly pass the library of functions, by stating up-front that the type itself only has one such library. We pass the library as a "constraint", so instead of EqOrd x -> [x] -> [x] you write Ord x => [x] -> [x] with the "fat arrow" of constraints. But secretly it means "when you ask me to use the < function on two values of type x, I know implicitly what library to get that function from and will get that function for you."
Now: you have one type, Shape, so you don't need typeclasses. (Go back to the first paragraph above: Type classes are collections of unrelated types which can do the same things.
If you want to do type classes then instead of the sum-type for Shape, let's define n-dimensional vectors of different types:
class Vector v where
(*.) :: (Num r) => r -> v r -> v r
(.+.) :: (Num r) => v r -> v r -> v r
norm :: (Num r, Floating r) => v r -> r
-- another advantage of type classes is *default declarations* like:
(.-.) :: (Num r) => v r -> v r -> v r
v1 .-. v2 = v1 .+. (-1 *. v2)
data V2D r = V2D r r deriving (Eq, Show)
instance Vector V2D where
s *. V2D x y = V2D (s * x) (s * y)
V2D x1 y1 .+. V2D x2 y2 = V2D (x1 + x2) (y1 + y2)
norm (V2D x y) = sqrt (x^2 + y^2)
data V3D r = V3D r r r deriving (Eq, Show)
instance Vector V3D where
s *. V3D x y z = V3D (s * x) (s * y) (s * z)
V3D x1 y1 z1 .+. V3D x2 y2 z2 = V3D (x1 + x2) (y1 + y2) (z1 + z2)
norm (V3D x y z) = sqrt (x^2 + y^2 + z^2)
Then we can write things like:
newtype GeneralPolygon v r = Poly [v r]
perimeter :: (Num r, Floating r, Vector v) -> GeneralPolygon v r -> r
perimeter (Poly []) = 0
perimeter (Poly (x : xs)) = foldr (+) 0 (map norm (zipWith (.-.) (x : xs) (xs ++ [x])))
translate :: (Vector v, Num r) => GeneralPolygon v r -> v r -> GeneralPolygon v r
translate (Poly xs) v = Poly (map (v .+.) xs)
Making Typeclasses Work For You
Now if you really want to, you can also unwrap your sum-type data declaration into a bunch of data declarations:
data Line = Line Point2d Point2d deriving (Eq, Show)
data Square = Square Point2d Point2d deriving (Eq, Show)
data Triangle = Triangle Point2d Point2d Point2d deriving (Eq, Show)
Now you can do something simple like:
class Shape s where
perim :: s -> Int
move :: s -> Vector2d -> s
Although I should say, you'll run into a problem when you want to do square roots for perimeters (sqrt is in the Floating typeclass, which Int does not have functions for, you'll want to change Int to Double or something).
Related
I want to write base implementation for Vertex.
data Point a = Point a a
class XY c where
x :: c a -> a
y :: c a -> a
class XY c => Vertex c where
translate :: c a -> c a -> c a
scale :: a -> c a -> c a
rotate :: a -> c a -> c a
instance XY Point where
x (Point first second) = first
y (Point first second) = second
instance Vertex Point where
translate xy1 xy2 = Point (x xy1 + x xy2) (y xy1 + y xy2)
scale a xy = Point ((*a) $ x xy) ((*a) $ y xy)
rotate a xy = Point (x * cosA - y * sinA) (x * sinA + y * cosA) where
cosA = cos a
sinA = sin a
I have to create instance of typeclass Vertex with implementation of Floating typeclass in Point type parameter.
If i implement it like instance (Floating a) => Vertex Point a where i get:
Expected kind ‘* -> Constraint’,
but ‘Vertex Point’ has kind ‘Constraint’
What is the correct way to write it in Haskell?
Aww. This well-known problem is a pet peeve of mine. The correct™ solution is to make the XY and Point classes not for parametric types. The scalar argument becomes an associated type synonym, and everything works easily:
{-# LANGUAGE TypeFamilies #-}
class XY p where
type Component p :: *
x :: p -> Component p
y :: p -> Component p
class XY p => Vertex p where
translate :: p -> p -> p
scale :: Component p -> p -> p
rotate :: Component p -> p -> p
N.B. In fact you could even consider simplifying this to always use the same component type, since you'll likely never need anything else:class XY p where
x :: p -> Double
y :: p -> Double
class XY p => Vertex p where
translate :: p -> p -> p
scale :: Double -> p -> p
rotate :: Double -> p -> p
With the non-parametric form, you can now easily add a number-type constraint exactly where it's needed, namely in the instance Vertex Point instance:
instance XY (Point a) where
type Component (Point a) = a
x (Point xc _) = xc
y (Point _ yc) = yc
instance Floating a => Vertex (Point a) where
translate xy1 xy2 = Point (x xy1 + x xy2) (y xy1 + y xy2)
scale a xy = Point ((*a) $ x xy) ((*a) $ y xy)
rotate a xy = Point (x * cosA - y * sinA) (x * sinA + y * cosA)
where cosA = cos a
sinA = sin a
For some reason†, most people however prefer to make classes for geometric entities parametric over the scalar type, which is not only completely unnecessary but also un-geometric, because proper geometry is emphatically not depended of an actual basis decomposition.
†Actually I'm fairly certain what the reason is: Edward Kmett's decision to use parameterised types in the linear library. He should have known better, especially since Conal Elliott's vector-space library, which does it the right way, has been around for longer already.
The following version is corrected so that it compiles:
data Point a = Point a a
class XY c where
x :: c a -> a
y :: c a -> a
class XY c => Vertex c where
translate :: (Num a) => c a -> c a -> c a
scale :: (Num a) => a -> c a -> c a
rotate :: (Floating a) => a -> c a -> c a
instance XY Point where
x (Point first second) = first
y (Point first second) = second
instance Vertex Point where
translate xy1 xy2 = Point (x xy1 + x xy2) (y xy1 + y xy2)
scale a xy = Point ((*a) $ x xy) ((*a) $ y xy)
rotate a xy = Point ((x xy) * cosA - (y xy) * sinA) ((x xy) * sinA + (y xy) * cosA) where
cosA = cos a
sinA = sin a
There were only 2 changes needed, in fact:
I have added type constraints on a for the methods of the XY class. Otherwise, you can't use functions such as + which you have in the implementation of the instance for Point. (GHC actually makes this exact suggestion in one of the error messages it throws when trying to compile your version.) Note that these have to go on the class, not the instance, because the instance declaration makes no mention of the type a (even though the implementation does). If you don't put the constraints in the class then the methods are expected to work for all possible types a.
x and y are in fact functions, so you can't multiply them with numbers like sinA. I suspect you just got confused here and could have figured out what to do - you needed to apply them to the xy (the "point" itself) to get the "x" and "y" "co-ordinates".
So actually you were pretty close, and just needed to pay attention to what the compiler was telling you. GHC's error messages can seem a bit obscure when you're new to Haskell, but with a bit of practice you soon see that they're (often, although not always) quite helpful.
This is something I have been confused about for a while and I am not sure how I can learn more about it. Let's say I have the following program:
main :: IO ()
main = do
x <- liftM read getLine
y <- liftM read getLine
print (x % y)
If I run this with the input 6 and 2, it will print 3 % 1.
At what point does the simplification happen (namely the division by the gcd)? Is it implemented in show? If so, then is the underlying representation of the rational still 6 % 2? If not, then does (%) do the simplification? I was under the impression that (%) is a data constructor, so how would a data constructor do anything more than "construct"? More importantly, how would I actually go about doing similar things with my own data constructors?
I appreciate any help on the topic.
Ratio is actually implemented in GHC.Real (on GHC, obviously), and is defined as
data Ratio a = !a :% !a deriving (Eq)
The bangs are just there for strictness. As you can see, the function % is not a data constructor, but :% is. Since you aren't supposed to construct a Ratio directly, you use the % function, which calls reduce.
reduce :: (Integral a) => a -> a -> Ratio a
{-# SPECIALISE reduce :: Integer -> Integer -> Rational #-}
reduce _ 0 = ratioZeroDenominatorError
reduce x y = (x `quot` d) :% (y `quot` d)
where d = gcd x y
(%) :: (Integral a) => a -> a -> Ratio a
x % y = reduce (x * signum y) (abs y)
The rule is that if an operator starts with a colon :, then it is a constructor, otherwise it is just a normal operator. In fact, this is part of the Haskell standard, all type operators must have a colon as their first character.
You can just look at the source to see for yourself:
instance (Integral a) => Num (Ratio a) where
(x:%y) + (x':%y') = reduce (x*y' + x'*y) (y*y')
(x:%y) - (x':%y') = reduce (x*y' - x'*y) (y*y')
(x:%y) * (x':%y') = reduce (x * x') (y * y')
negate (x:%y) = (-x) :% y
abs (x:%y) = abs x :% y
signum (x:%_) = signum x :% 1
fromInteger x = fromInteger x :% 1
reduce :: (Integral a) => a -> a -> Ratio a
reduce _ 0 = ratioZeroDenominatorError
reduce x y = (x `quot` d) :% (y `quot` d)
where d = gcd x y
I've been programming in C-type and Lisp-type languages for a few decades and Haskell for a few years. Now in order to further my understanding of typeclasses and other nifty more advanced features of Haskell, such as parallelism, I've been trying to create a new data type with matrix semantics, for now piggy-backed on top of the library's Array type.
I am using the latest Haskell Platform 2013.2.0.0 with the included ghc.
newtype Matrix a = Matrix (Array (Int,Int) a)
(I understand that data would work instead of newtype, but that in this instance, you get the same semantics with better performance using newtype instead).
Some simple functions to create an identity matrix, for example, work just fine:
unityMatrix:: (Num a) => Int -> Matrix a
unityMatrix d = Matrix $ let b = ((0,0),(d-1,d-1)) in array b
[((i,j),if i==j then 1 else 0)|(i,j)<-range b]
So does creating a basic show function:
instance Show a => Show (Matrix a) where
show (Matrix x) =
let
b = bounds x
rb = ((fst.fst) b, (fst.snd) b)
cb = ((snd.fst) b, (snd.snd) b)
in
intercalate "\n" [unwords ([show (x!(r,c))|c<-range cb])|r<-range rb]
Then, for the regular arithmetic operators to work, I add this:
instance Num a => Num (Matrix a) where
fromInteger x =
let
b = ((0,0),(0,0))
in Matrix $ array b
[((i,j),if i == j then (fromInteger x) else 0)|(i,j)<-range b]
(Matrix x) + (Matrix y) =
let
b = bounds x
in
if b /= bounds y then error "Unmatched matrix addition" else Matrix $ array b
[(ij,x!ij + y!ij)|ij<-range b]
signum (Matrix x) =
let
b = bounds x
in Matrix $ array b
[(ij,signum (x!ij))|ij<-range b]
abs (Matrix x) =
let
b = bounds x
in Matrix $ array b
[(ij,abs (x!ij))|ij<-range b]
(Matrix x) - (Matrix y) =
let
b = bounds x
in
if b /= bounds y then error "Unmatched matrix subtraction" else Matrix $ array b
[(ij,x!ij - y!ij)|ij<-range b]
(Matrix x) * (Matrix y) =
let
b = (((fst.fst.bounds) x, (fst.snd.bounds) x),((snd.fst.bounds) y, (snd.snd.bounds) y))
kb = ((snd.fst.bounds) x, (snd.snd.bounds) x)
in
if kb /= ((fst.fst.bounds) y, (fst.snd.bounds) y) then error "Unmatched matrix multiplication" else Matrix $ array b
[((i,j),sum [(x!(i,k)) * (y!(k,j))|k<-range kb])|(i,j)<-range b]
(My apologies if the indentation is screwed up here--the actual code is properly indented and compiles.)
So far, so good, though it is a little annoying to have to define a fromInteger function which does not really have any meaning in matrix semantics, but creating a 1x1 matrix with the value is as reasonable as anything else.
My problem is trying to get proper semantics for multiplication of a scalar (i.e., a type of the Num typeclass) with a matrix. By mathematical convention that means just element-wise multiplication with the scalar.
However, no matter what syntax I try, I don't get the right result. By default this implementation just promotes, e.g., an Int to a Matrix using fromInteger which (unless the Matrix is already 1x1) results in a "Unmatched matrix multiplication" error. I have tried all alternative syntaxes I can think of to define a alternate code for this type of multiplication without success, such as:
(Matrix x) * (y::Int) =
or
(Matrix x) * (Num y) =
or
(*):: (Num a) => Matrix -> a -> Matrix
But all of these give me various syntax errors.
How should I go about defining the scalar by matrix multiplication so that it does what you would expect it to do? I feel that enabling the non-standard pattern guard feature might help, but I'm not quite sure how to use that correctly in this context.
I realize that I could just special case the Matrix multiplication to allow multiplication of any Matrix with a 1x1 Matrix which I guess would work. But that would be (a) inelegant, (b) un-Haskell-y, (c) probably inefficient as it would require every scalar to be wrapped into a matrix before being multiplied, and (d) would permit some code (such as multiplication of an arbitrary matrix with any 1x1 matrix) to run when it should result in a n error.
I also understand that there are probably excellent Matrix implementations out there which somehow sidestep this problem. But using them would defeat my purpose to learn.
This is the type of the standard (*) operator:
(*) :: Num a => a -> a -> a
In other words, the two arguments to this operator have to be the same type, so what you are asking for is not possible as it stands.
I can see a couple of options:
As suggested in the comments, define your own (*), perhaps with a type class to go with it that standard types like Integer can also be a member of. This answer might provide some inspiration.
Add scalars to your matrix type:
data Matrix a = Scalar a | Matrix (Array (Int,Int) a)
(perhaps the name could now be improved - also note that now it has to be data rather than newtype. I doubt the performance difference will matter in practice.)
Just an attemp to extend Ganesh's answer.
We could redefine Scalar matrix as Unity matrix of unindentidied size.
import Data.List (transpose)
data Matrix a = Matrix [[a]] | UnityMatrix a deriving (Show)
instance Functor Matrix where
fmap f (Matrix x) = Matrix $ fmap (fmap f) x
fmap f (UnityMatrix x) = UnityMatrix $ f x
fmap2::(Num a) => (a->a->b)->Matrix a->Matrix a->Matrix b
fmap2 f (Matrix x) (Matrix y) = Matrix $ zipWith ( zipWith f ) x y
fmap2 f m#(Matrix x) u#(UnityMatrix y) = fmap2 f m $ expandUnity u
fmap2 f u#(UnityMatrix y) m#(Matrix x) = fmap2 f (expandUnity u) m
fmap2 f (UnityMatrix x) (UnityMatrix y) = UnityMatrix $ f x y
expandUnity (UnityMatrix a) = Matrix [replicate i 0 ++ a : repeat 0| i <-[0..]]
instance Num a => Num (Matrix a) where
fromInteger = UnityMatrix . fromInteger
signum = fmap signum
abs = fmap abs
(+) = fmap2 (+)
(-) = fmap2 (-)
(Matrix x) * (Matrix y) = Matrix [[sum $ zipWith (*) a b | b <- transpose y ]| a <- x ]
m#(Matrix x) * (UnityMatrix y) = fmap (*y) m
(UnityMatrix y) * m#(Matrix x) = fmap (y*) m
(UnityMatrix x) * (UnityMatrix y) = UnityMatrix $ x * y
main = print $ 3 * Matrix [[1,2,3],[4,5,6]] + 2
This code contains lot of repeating - but this in case if any of (+) , (-) or (*) operators in base type is not commutative.
Also underlying type changed to list of lists instead of matrix. But this is only to ease of idea demo.
The "semantics you're looking for" are just inconsistent with how the Haskell Num class is defined. There is no such thing as data promotion in Haskell (for very good reasons that are hard to explain but easy found out the more experience you gain with Haskell).
I'd not recommend defining a new *, nor your own operator name that has the signature a -> Matrix a -> Matrix a, it would be confusing. Rather, you should look where this operation is already defined: as user5402 has it, this is a scalar multiplication. This is obviously not just meaningful for matrices / linear operators, but already to vectors. And behold, here is the class for vector spaces!
But like suggested by Ganesh and Odomontois for Num, you'll also need to expand you data type to make proper use of this. The basic problem is that matrix multiplication is really only defined for matching covariant-contravariant dimensions, but your approach has no way to ensure this. Ideally, the type checker should infer the correct dimension, but instead of that you can have a special case, not just for the identity but general diagonal matrices.
data LinOp a = Diagonal a
| GMatrix (Matrix a)
instance Functor LinOp where
fmap f (Diagonal a) = Diagonal (f a)
fmap f (GMatrix m) = GMatrix $ fmap f m
instance (Num a) => AdditiveGroup (Matrix a) where
zeroV = Diagonal 0
negateV = fmap negate
(Diagonal x) ^+^ (Diagonal y) = Diagonal $ x+y
...
instance (Num a) => VectorSpace (Matrix a) where
type Scalar (Matrix a) = a
μ *^ m = fmap (μ*) m
instance (Num a) => Num (Matrix a) where
fromInteger = Diagonal . fromInteger
(+) = (^+^)
...
Does this work?
scalarMult :: Num a => a -> Matrix a -> Matrix a
scalarMult c (Matrix x) = Matrix $ array (range x) [(ij,(x!ij) * c) | ij <- range x]
In general you can't implement scalar multiplication with fromInteger and ordinary matrix multiplication because you don't know what size matrix to create - it will depend on its context.
However, you could possibly use this approach if you kept track of the matrix size in the type system. Then the compiler could infer what size to promote scalars to, and it also could detect dimension mismatches for matrix operations at compile time.
I'd like to define a tuple (x, y) as an instance of Enum class, knowing that both x and y are instances of Enum. A following try:
instance (Enum x, Enum y) => Enum (x, y) where
toEnum = y
enumFrom x = (x, x)
only results in error (y not in scope). I'm new to Haskell, could somebody explain how to declare such an instance?
instance (Enum x, Enum y) => Enum (x, y) where
In the above line, x and y are both types (type variables).
toEnum = y
enumFrom x = (x, x)
In the above two lines, x and y are both values ((value) variables). y-as-a-value has not been defined anywhere, that's what it not being in scope means.
As to how to declare such an instance, I'm not sure how you'd want fromEnum and toEnum to behave, for example.
Not a good idea if you ask me, but anyway —
To make an instance of a type class, you need to look at the signatures.
class Enum a where
succ :: a -> a
pred :: a -> a
toEnum :: Int -> a
fromEnum :: a -> Int
enumFrom :: a -> [a]
enumFromThen :: a -> a -> [a]
enumFromTo :: a -> a -> [a]
enumFromThenTo :: a -> a -> a -> [a]
So in your case
toEnum :: Int -> (x, y)
but toEnum = y isn't even defined, because y is just a type, not a value or constructor. Possibilities would be
toEnum n = (toEnum 0, toEnum n)
or
toEnum n = (toEnum n, toEnum n)
or
toEnum n = (toEnum $ n`div`2, toEnum $ (n+1)`div`2)
As for enumFrom, your version has signature
enumFrom :: a -> (a,a)
but we need
enumFrom :: (x,y) -> [(x,y)]
what definition is suitable depends on how toEnum was defined; for my first suggestion it would be
enumFrom (x,y) = [ (x,y') | y' <- enumFrom y ]
Reading Dietrich Epp's comment
It's not actually possible to create a useful Enum (x, y) from Enum x and Enum y. You'd need additional context, like Bounded x, Bounded y, Enum x, Enum y => Enum (x, y).
I thought about ways it could actually be done meaningfully. It seems possible sure enough, a bijection ℤ → ℤ2 exists. My suggestion:
[ ...
, (-3,-3), (-3,-2), (-2,-3), (-3,-1), (-1,-3), (-3,0), (0,-3), (-3,1), (1,-3), (-3,2), (2,-3), (-3,3), (3,-3)
, (-2,3), (3,-2), (-1,3), (3,-1)
, (-2,-2), (-2,-1), (-1,-2), (-2,0), (0,-2), (-2,1), (1,-2), (-2,2), (2,-2)
, (-1,2), (2,-1)
, (-1,-1), (-1,0), (0,-1), (-1,1), (1,-1)
, (0,0)
, (1,0), (0,1), (1,1)
, (2,0), (0,2), (2,1), (1,2), (2,2)
, (3,0), (0,3), (3,1), (1,3), (3,2), (2,3), (3,3)
, ... ]
Note that this reduces to a bijection ℕ → ℕ2 as well, which is important because some Enum instances don't go into the negative range and others do.
Implementation:
Let's make a plain (Int,Int) instance; it's easy to generalize that to your desired one. Also, I'll only treat the positive cases.
Observe that there are k^2 tuples between (0,0) and (excluding) (k,0). All other tuples (x,y) with max x y == k come directly after it. With that, we can define fromEnum:
fromEnum (x,y) = k^2 + 2*j + if permuted then 1 else 0
where k = max x y
j = min x y
permuted = y>x
for toEnum, we need to find an inverse of this function, i.e. knowing fromEnum -> n we want to know the parametes. k is readily calculated as floor . sqrt $ fromIntegral n. j is obtained similarly, simply with div 2 of the remainder.
toEnum n = let k = floor . sqrt $ fromIntegral n
(j, permdAdd) = (n-k^2) `divMod` 2
permute (x,y) | permdAdd>0 = (y,x)
| otherwise = (x,y)
in permute (k,j)
With fromEnum and toEnum, all the other functions are rather trivial.
So I'm trying to make a type for variable-length tuples, basically as a prettier version of Either a (Either (a,b) (Either (a,b,c) ...)) and Either (Either (Either ... (x,y,z)) (y,z)) z.
{-# LANGUAGE TypeOperators, TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}
module Temp where
-- type level addition
data Unit
data Succ n
class Summable n m where
type Sum n m :: *
instance Summable Unit m where
type Sum Unit m = Succ m
instance Summable n m => Summable (Succ n) m where
type Sum (Succ n) m = Succ (Sum n m)
-- variable length tuple, left-to-right
data a :+ b = a :+ Maybe b
infixr 5 :+
class Prependable t r s where
type Prepend t r s :: *
prepend :: r -> Maybe s -> Prepend t r s
instance Prependable Unit x y where
type Prepend Unit x y = x :+ y
prepend = (:+)
instance Prependable n x y => Prependable (Succ n) (w :+ x) y where
type Prepend (Succ n) (w :+ x) y = w :+ Prepend n x y
prepend (w :+ Nothing) _ = w :+ Nothing
prepend (w :+ Just x) y = w :+ Just (prepend x y)
-- variable length tuple, right-to-left
data a :- b = Maybe a :- b
infixl 5 :-
class Appendable t r s where
type Append t r s :: *
append :: Maybe r -> s -> Append t r s
instance Appendable Unit x y where
type Append Unit x y = x :- y
append = (:-)
instance Appendable n x y => Appendable (Succ n) x (y :- z) where
type Append (Succ n) x (y :- z) = Append n x y :- z
append _ (Nothing :- z) = Nothing :- z
append x (Just y :- z) = Just (append x y) :- z
However, the compiler seems unable to infer the phantom type parameter of prepend or append in the recursive cases:
Temp.hs:32:40:
Could not deduce (Prepend t1 x y ~ Prepend n x y)
from the context (Prependable n x y)
bound by the instance declaration at Temp.hs:29:10-61
NB: `Prepend' is a type function, and may not be injective
In the return type of a call of `prepend'
In the first argument of `Just', namely `(prepend x y)'
In the second argument of `(:+)', namely `Just (prepend x y)'
Temp.hs:49:34:
Could not deduce (Append t0 x y ~ Append n x y)
from the context (Appendable n x y)
bound by the instance declaration at Temp.hs:46:10-59
NB: `Append' is a type function, and may not be injective
In the return type of a call of `append'
In the first argument of `Just', namely `(append x y)'
In the first argument of `(:-)', namely `Just (append x y)'
Is there anything I can do to help the compiler make this inference?
The important part of the error message here is the:
NB: `Prepend' is a type function, and may not be injective
What does this mean? It means that there might be multiple instance Prependable such that their type Prepend ... = a, so that if you infer some Prepend to be a, you don't necessarily know which instance it belongs to.
You can solve this by using data types in type families, which have the advantage that you don't deal with type functions, which are surjective but might be injective, and instead with type "relations", which are bijective (So every Prepend type can only belong to one type family, and every type family has a distinct Prepend type).
(If you want me to show a solution with data types in type families, leave a comment! Basically, just use a data Prepend instead of type Prepend)
The solution I came up with was to add a dummy argument to tie prepend and append to the phantom parameter:
-- as above, except...
unsucc :: Succ n -> n
unsucc _ = undefined
class Prependable t r s where
type Prepend t r s :: *
prepend :: t -> r -> Maybe s -> Prepend t r s
instance Prependable Unit x y where
type Prepend Unit x y = x :+ y
prepend _ = (:+)
instance Prependable n x y => Prependable (Succ n) (w :+ x) y where
type Prepend (Succ n) (w :+ x) y = w :+ Prepend n x y
prepend _ (w :+ Nothing) _ = w :+ Nothing
prepend t (w :+ Just x) y = w :+ Just (prepend (unsucc t) x y)
class Appendable t r s where
type Append t r s :: *
append :: t -> Maybe r -> s -> Append t r s
instance Appendable Unit x y where
type Append Unit x y = x :- y
append _ = (:-)
instance Appendable n x y => Appendable (Succ n) x (y :- z) where
type Append (Succ n) x (y :- z) = Append n x y :- z
append _ _ (Nothing :- z) = Nothing :- z
append t x (Just y :- z) = Just (append (unsucc t) x y) :- z