Создать поток событий полиморфных функций - возможно? Если да, то как?

В настоящее время я изучаю FRP с реактивным бананом и хотел создать поток случайных функций. Я придумал это:

-- | take number generator, and some pulse event stream, generate random function stream
mkRandom :: (Random a,RandomGen g) => g -> Event t b -> Event t ((a,a) -> a)
mkRandom rng es = (\f -> \r -> fst $ f r) <$> (accumE first $ next <$> es)
  where first = flip randomR rng
        next _ prev range = randomR range g
          where (a,g) = prev range

Вроде работает, могу использовать вот так:

randFuncs = mkRandom rnd (pulse 1000 time)
some = ($ (0,10::Int)) <$> randFuncs

Но, конечно, когда я пытаюсь поделиться этим потоком для генерации чисел другого типа:

some2 = ($ (0,10::Double)) <$> randFuncs

Проверка типов жалуется, я понимаю. Затем я попытался обобщить функцию до следующего:

mkRandom :: (RandomGen g) => g -> Event t b -> Event t (forall a. Random a => (a,a) -> a)

Затем GHC пожаловался на нелегальную полиморфную подпись и на то, хочу ли я включить ImpredicativeTypes. Я сделал это и довольно долго пытался аннотировать все, чтобы это работало, но GHC всегда жаловался, что он не может соответствовать типам.

У меня вопрос - можно ли делать то, что я хочу? Мне действительно нужны ImpredicativeTypes для этого или я просто делаю это неправильно?

Я думал, что для этого должно хватить RankNTypes, но у меня пока нет опыта работы с такими расширениями.

Заранее спасибо!

РЕДАКТИРОВАТЬ:

Для записи, теперь мое решение, основанное на полезном ответе, таково:

newtype RandomSource = Rand { getRand :: forall a. (Random a) => (a,a) -> [a] }

-- | take number generator and some pulse event stream, generate randomness stream
mkRandom :: RandomGen g => g -> Event t a -> Behavior t RandomSource
mkRandom rng es = fst <$> (accumB (next id (id,rng)) $ next <$> es)
  where next _ (_,rng) = (Rand $ flip randomRs g1, g2)
          where (g1,g2) = split rng

-- | take a rand. source, a range and a pulse, return stream of infinite lists of random numbers
randStream :: Random a => Behavior t RandomSource -> (a,a) -> Event t b -> Event t [a]
randStream funcs range pulse = ($ range) . getRand <$> funcs <@ pulse

person apirogov    schedule 07.10.2015    source источник
comment
Мне сказали, что reactive-banana 1.0 (текущий master) упростит типы нетривиальными и существенными способами, например, не потребует RankNTypes (в некоторых сценариях?), Так что вы можете попробовать.   -  person Erik Kaplun    schedule 07.10.2015
comment
@ErikAllik Изменения API для reactive-banana 1.0 в основном касаются параметра типа t. Данный вопрос не имеет отношения к этому.   -  person Heinrich Apfelmus    schedule 08.10.2015


Ответы (1)


ImpredicativeTypes - невероятно хрупкое расширение, которое на самом деле не поддерживается и не поддерживается и поэтому продолжает ломаться в новых версиях GHC.

Намного лучший рабочий вариант - использовать RankNTypes вместе с newtype оболочкой:

newtype PolyRandFun = PR { getPR :: forall a. Random a => (a,a) -> a) }

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

К сожалению, я предвижу другую проблему в этом случае. Различные экземпляры Random a используют свой генератор случайных чисел разное количество, а в случае, например, Integer количество примитивных случайных чисел, генерируемых для Integer результата, будет даже зависеть от размера диапазона. Таким образом, вы не можете получить следующий g, не зная, какой тип и диапазон используются при фактическом вызове ваших функций.

К счастью, в System.Random API есть функция, которая может обойти это: split дает вам новый генератор случайных чисел, который можно передать в субвычисления, когда вам действительно нужно сгенерировать несколько случайных значений совершенно независимо.

person Ørjan Johansen    schedule 07.10.2015
comment
Увы, я могу подтвердить, что ImpredicativeTypes очень хрупкий, но можете ли вы сказать, почему его так мало обслуживают? Я считаю, что это должно быть довольно естественным продолжением RankN. - person leftaroundabout; 07.10.2015
comment
@leftaroundabout Объяснение Саймона П.Дж. - person Ørjan Johansen; 07.10.2015
comment
@leftaroundabout, лично я не могу, но мне кажется, что это серьезная проблема. Средство проверки типов GHC, по-видимому, в корне не способно делать это правильно, и никто не знает, как написать средство проверки типов, которое делает это правильно, предлагает все другие приятные вещи и выдает неопределенно удобочитаемые сообщения об ошибках. - person dfeuer; 07.10.2015
comment
Спасибо! Необходимая распаковка немного неудовлетворительна, но работает! - person apirogov; 07.10.2015