Экземпляр семейства полиморфных типов в Haskell

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

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

{-# 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.

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


person Jeremy Silver    schedule 16.02.2019    source источник
comment
Правильно: вы должны делать отдельные объявления экземпляров для каждого экземпляра JointDist. Тем не менее, здесь есть несколько запахов кода, которые заставляют меня думать, что гораздо более простой дизайн оправдан. Возможно, можно было бы дать лучший совет, если бы вы могли ответить на вопрос: существует ли экземпляр JointDist, который не изоморфен TrivialDist (и если да, то как он выглядит)?   -  person Daniel Wagner    schedule 16.02.2019
comment
В качестве альтернативы вы можете проверить некоторые библиотеки, которые предоставляют вам случайные переменные. Я предпочитаю MonadBayes, который реализует монаду вероятности, позволяющую осуществлять выборку, обусловливание и логический вывод.   -  person C. Hammill    schedule 17.02.2019


Ответы (1)


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

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 (в противном случае мы получим ошибку).

person Jeremy Silver    schedule 17.02.2019