Dane:Czy Haskell obsługuje zamknięte typy polimorficzne?
newtype PlayerHandle = PlayerHandle Int deriving (Show)
newtype MinionHandle = MinionHandle Int deriving (Show)
newtype WeaponHandle = WeaponHandle Int deriving (Show)
W poniższym kodzie, chciałbym handle
być dokładnie jeden z trzech rodzajów: PlayerHandle
, MinionHandle
i WeaponHandle
. Czy można to zrobić w Haskell?
data Effect where
WithEach :: (??? handle) => [handle] -> (handle -> Effect) -> Effect -- Want `handle' to be under closed set of types.
Poniżej zbyt uciążliwe:
data Effect' where
WithEachPlayer :: [PlayerHandle] -> (PlayerHandle -> Effect) -> Effect
WithEachMinion :: [MinionHandle] -> (MinionHandle -> Effect) -> Effect
WithEachWeapon :: [WeaponHandle] -> (WeaponHandle -> Effect) -> Effect
EDIT:
Ørjan Johansen zaproponował przy użyciu zamkniętych typu rodzin, które rzeczywiście robi mi to krok bliżej do tego, co chcę. Problem mam korzystania z nich jest to, że nie może wydawać się, aby napisać następujące:
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
Tutaj GHC narzeka, że nie można wywnioskować, że uchwyt jest instancją Show
. Jestem niezdecydowany, aby rozwiązać ten problem, przenosząc ograniczenie Show
do konstruktora WithEach
z różnych powodów. Należą do nich modułowość i skalowalność. Czy rozwiązałoby to coś takiego, jak rodzina danych zamkniętych (jak wiem, rodzinne mapowania nie są iniekcyjne ... Czy to jest problem nawet w przypadku zamkniętych?)
Uważam to bardzo ciekawy i masz już moje upvote ale mam nadzieję, że nie przeszkadza ci na pytanie: dlaczego nie wystarczy użyć typu sum za swoje obsługi - jestem pewien, że masz swoje powody, ale przykład tutaj wydaje się krzyczeć na to podstawowe rozwiązanie. – Carsten
@Carsten: Głównie dlatego, że miałem nadzieję, że był lepszy sposób. Nigdy wcześniej nie używałam zamkniętych rodzin typów (zapomniałem, że GHC już je wspierał). W tym momencie, myślę, że mógłbym po prostu użyć trzech różnych konstruktorów i gdy wzorzec ich dopasowujący, mogę przekazać je bezpośrednio do 'enactEffect :: (Show h) => [h] -> (h -> Effect) -> IO() '. To pozwoli mi radzić sobie z bardziej złożonymi ograniczeniami niż 'Show' (przy założeniu, że' Show' jest warunkiem wstępnym dla konstruktora 'WithEach'), włączając moduły prywatne. –
tak długo, jak nie chcesz rozdzielać się z innym edytorem obsługi IMO, nie ma * lepszego * sposobu [to] (https://gist.github.com/CarstenKoenig/9c7354b88befcc1db1d8) wydaje się być dużo łatwiejszy (dla mnie;)) – Carsten