So I have a situation very similar to this (much simplified) code:
import Data.Maybe
import Data.List
data Container a = Container [a]
-- Assumption: an Element can only obtained from a Container. The operation
-- guarentees the Element is in the Container.
data Element a = Element (Container a) a
-- This operation is only valid for Elements that have the same Container.
before :: Eq a => Element a -> Element a -> Bool
before (Element (Container xs) x) (Element (Container ys) y)
| xs == ys = fromJust (elemIndex x xs) < fromJust (elemIndex y xs)
| otherwise = error "Elements from different Containers"
How can I use Haskell's type system (or GHC extensions) to restrict before
and similar operations from taking Element
s from different Container
s? I've been looking into GADTs
and DataKinds
but, well, that's going to take a long while and I could use some suggestions or pointers. (Another idea I've thought of but haven't worked out: use a similar trick as the ST
monad's s
parameter...)
Am I too pessimistic if I conclude that this would require a dependently typed language? Because, as my limited understanding of dependent typing goes, I think that I'm trying to index the Element
type by values of the Container
type.
EDIT: Just for extra color, this all ultimately arises because I'm trying to define something very much like this:
import Data.Function
import Data.Ix
-- I want to use Elements as Array indexes; so Elements need to know
-- their Container to calculate their Int
instance Eq a => Ix (Element a) where
-- Ignore how crap this code is
range (l, u) = map (getSibling l) [getIndex l .. getIndex u]
index (l,u) i = index (getIndex l, getIndex u) (getIndex i)
inRange (l,u) i = inRange (getIndex l, getIndex u) (getIndex i)
getIndex :: Eq a => Element a -> Int
getIndex (Element (Container xs) x) = fromJust $ elemIndex x xs
getList :: Element a -> [a]
getList (Element (Container xs) _) = xs
getSibling :: Element a -> Int -> Element a
getSibling (Element (Container xs) _) i = Element (Container xs) (xs!!i)
instance Eq a => Eq (Element a) where
(==) = (==) `on` getIndex
instance Eq a => Ord (Element a) where
compare = compare `on` getIndex
All of this code requires that you never "mix" Element
s from different Container
s.