Экземпляр семейства полиморфных типов в Хаскеле - PullRequest
0 голосов
/ 16 февраля 2019

Я пытаюсь написать код для имитации случайных величин, и я хочу, чтобы все было как можно более полиморфным.Это может включать использование семейств типов, которые являются совершенно новыми для меня.

Вот упрощенная версия моего кода:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}


data TrivialDist a = Trivial a

getVal :: TrivialDist a -> a
getVal (Trivial x) = x

class JointDist d a where
    toTrivialDist :: d a -> TrivialDist [a]

data TrivialJointDist a = TrivialJoint [a]

instance JointDist TrivialJointDist a where
    toTrivialDist :: TrivialJointDist a -> TrivialDist [a]
    toTrivialDist (TrivialJoint xs) = Trivial xs

class Simulable s a where
    type Samp s a :: *
    -- generates infinite stream of random samples from a distribution
    samples :: s a -> IO [Samp s a]
    -- generates a single random sample from a distribution
    sample :: s a -> IO (Samp s a)
    sample = fmap head . samples

instance Simulable TrivialDist a where
    type Samp TrivialDist a = a
    samples :: TrivialDist a -> IO [a]
    samples (Trivial x) = return $ repeat x

instance (JointDist d a) => Simulable d a where
    type Samp d a = [a]
    samples :: d a -> IO [[a]]
    samples = samples . toTrivialDist

Когда я загружаю его в ghci, я получаю этоошибка:

test.hs:30:10: error:
    Conflicting family instance declarations:
      Samp TrivialDist a = a -- Defined at test.hs:30:10
      Samp d a = [a] -- Defined at test.hs:40:10
   |
30 |     type Samp TrivialDist a = a
   |          ^^^^^^^^^^^^^^^^^^^^^^
Failed, 0 modules loaded.

Эта проблема, похоже, похожа на найденную здесь (объяснение, по-видимому, состоит в том, что GHC не может различить типы на основе ограничений), но решение не былопредлагаемый.

Я могу получить код для компиляции, если я изменю объявление последнего экземпляра на:

instance Simulable TrivialJointDist a where
    type Samp TrivialJointDist a = [a]
    samples :: TrivialJointDist a -> IO [[a]]
    samples = samples . toTrivialDist

Однако тогда я потеряю преимущество полиморфизма, так как мне нужно было бы сделать отдельныйобъявления экземпляров для каждого подтипа JointDist d a.

Любая помощь очень ценится!

1 Ответ

0 голосов
/ 17 февраля 2019

Немного повозившись, думаю, я нашел решение, которое использует ограничения равенства вместо явных семейств типов:

class Simulable s a b where
    -- generates infinite stream of random samples from a distribution
    samples :: s a -> IO [b]
    -- generates a single random sample from a distribution
    sample :: s a -> IO b
    sample = fmap head . samples

instance (b ~ a) => Simulable TrivialDist a b where
    samples :: TrivialDist a -> IO [a]
    samples (Trivial x) = return $ repeat x

instance {-# OVERLAPPABLE #-} (JointDist d a, b ~ [a]) => Simulable d a b where
    samples :: d a -> IO [[a]]
    samples = samples . toTrivialDist

Прагма {-# OVERLAPPABLE #-} инструктирует GHC разрешить экземпляр TrivialDist a bперекрывать экземпляр d a b (в противном случае мы получим ошибку).

...