Дано:
newtype PlayerHandle = PlayerHandle Int deriving (Show)
newtype MinionHandle = MinionHandle Int deriving (Show)
newtype WeaponHandle = WeaponHandle Int deriving (Show)
В следующем коде я хотел бы, чтобы handle
был точно одним из трех типов: PlayerHandle
, MinionHandle
и WeaponHandle
. Возможно ли это сделать в Haskell?
data Effect where
WithEach :: (??? handle) => [handle] -> (handle -> Effect) -> Effect -- Want `handle' to be under closed set of types.
Слишком утомительно следующее:
data Effect' where
WithEachPlayer :: [PlayerHandle] -> (PlayerHandle -> Effect) -> Effect
WithEachMinion :: [MinionHandle] -> (MinionHandle -> Effect) -> Effect
WithEachWeapon :: [WeaponHandle] -> (WeaponHandle -> Effect) -> Effect
EDIT:
Ørjan Johansen предложил использовать замкнутые семейства типов, что действительно делает меня на шаг ближе к тому, что я хочу. Проблема, которую я использую, заключается в том, что я не могу написать следующее:
type family IsHandle h :: Constraint where
IsHandle (PlayerHandle) = ()
IsHandle (MinionHandle) = ()
IsHandle (WeaponHandle) = ()
data Effect where
WithEach :: (IsHandle handle) => [handle] -> (handle -> Effect) -> Effect
enactEffect :: Effect -> IO ()
enactEffect (WithEach handles cont) = forM_ handles $ \handle -> do
print handle -- Eeek! Can't deduce Show, despite all cases being instances of Show.
enactEffect $ cont handle
Здесь GHC жалуется, что не может вывести, что дескриптор является экземпляром Show
. Я не решаюсь это решить, перемещая ограничение Show
в конструкторе WithEach
по разным причинам. К ним относятся модульность и масштабируемость. Будет ли что-то вроде замкнутого семейства данных решить это (поскольку я знаю, что сопоставления семейства типов не являются инъективными... Это проблема даже с закрытыми?)