Итак, у меня есть ситуация, очень похожая на этот (намного упрощенный) код:
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"
Как я могу использовать систему типов Haskell (или расширения GHC), чтобы запретить before
и подобным операциям брать Element
из разных Container
? Я изучал GADTs
и DataKinds
, но это займет много времени, и я мог бы использовать некоторые предложения или указатели. (Еще одна идея, о которой я думал, но не реализовал: использовать аналогичный трюк, как параметр s
монады ST
...)
Не слишком ли я пессимистичен, если приду к выводу, что для этого потребуется язык с зависимой типизацией? Потому что, исходя из моего ограниченного понимания зависимой типизации, я думаю, что пытаюсь индексировать тип Element
по значениям типа Container
.
EDIT: Просто для дополнительного цвета, все это в конечном итоге возникает, потому что я пытаюсь определить что-то очень похожее на это:
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
Весь этот код требует, чтобы вы никогда не «смешивали» Element
из разных Container
.