2016-08-05 2 views
7

я заметил, что набор тестов для Data.Set только действительно определяет Arbitrary Set a благоразумно для a ~ Int, но, чтобы избежать GHC специальный ~ он используетКак определить экземпляр для приложения определенного типа в Haskell 98?

instance Enum a => Arbitrary (Set a) 

Как я могу убедиться, что только экземпляр Arbitrary (Set Int) используется без необходимости каких-либо расширений GHC ? В GHC-только код, я хотел бы использовать либо FlexibleInstances или GADTs, а затем либо

instance Arbitrary (Set Int) 

или

instance a ~ Int => Arbitrary (Set a) 

ответ

6

Это возможно с помощью идеи, я думаю, я впервые столкнулся в работе Олега Киселева, и который лежит в основе Control.Lens.Equality.

import Data.Functor.Identity 

class IsInt a where 
    fromIntF :: f Int -> f a 

instance IsInt Int where 
    fromIntF fx = fx 

toIntF :: IsInt a => g a -> g Int 
toIntF = unf . fromIntF . F $ id 

newtype F g a b = F {unf :: g b -> a} 

fromInt :: IsInt a => Int -> a 
fromInt = runIdentity . fromIntF . Identity 

toInt :: IsInt a => a -> Int 
toInt = runIdentity . toIntF . Identity 

Теперь я могу использовать

instance IsInt a => Arbitrary (Set a) 

и быть уверенным в том, что я действительно имеем дело с Int. Для удобства, я могу ограничить IsInt класс с любыми классами, мне нужно из которых Int является экземпляром:

class (Show a, Read a, Integral a, Arbitrary a) => IsInt a where ... 
+3

Может быть, стоит упомянуть: это дает гарантию, которую вы хотите вне системы, но не внутри него. То есть, GHC не будет выходить из 'IsInt a', что' a ~ Int'. Иногда это может потребовать раздражающих аннотаций дополнительного типа. Я не думаю, что это может помочь только в H98 (или H2010). –

+0

@ Даниэль Вагнер, да, это определенно уступает подходу GHC. Если ограничения равенства будут стандартизованы, я определенно их предпочту. Но «контейнеры» обычно стараются быть «переносимыми», насколько это возможно, поэтому, если я смогу сделать то, что мне нужно без расширения, я сделаю это. – dfeuer

+1

@ DanielWagner, я думаю, стоит также отметить, что эта реализация совместима с GHC, поскольку вы можете применять 'fromIntF' к' Refl :: Int: ~: Int', чтобы получить что-то типа 'IsInt a => Int: ~ : a'. Действительно, это будет работать для любого подобного типа равенства, независимо от того, как он вписывается в язык. – dfeuer

Смежные вопросы