创建多态函数的事件流 - 可能吗?如果有,怎么样?

api*_*gov 6 haskell reactive-banana

我目前正在学习反应性香蕉的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
Run Code Online (Sandbox Code Playgroud)

它似乎工作,我可以像这样使用它:

randFuncs = mkRandom rnd (pulse 1000 time)
some = ($ (0,10::Int)) <$> randFuncs
Run Code Online (Sandbox Code Playgroud)

但是,当然,当我尝试共享该流以生成不同类型的数字时:

some2 = ($ (0,10::Double)) <$> randFuncs
Run Code Online (Sandbox Code Playgroud)

类型检查器抱怨,我明白.然后我尝试将该函数概括为以下内容:

mkRandom :: (RandomGen g) => g -> Event t b -> Event t (forall a. Random a => (a,a) -> a)
Run Code Online (Sandbox Code Playgroud)

然后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
Run Code Online (Sandbox Code Playgroud)

Ørj*_*sen 7

ImpredicativeTypes 是一个非常脆弱的扩展,没有真正支持或维护,因此在新的GHC版本中不断进一步突破.

一个更好的工作选项是RankNTypesnewtype包装器一起使用:

newtype PolyRandFun = PR { getPR :: forall a. Random a => (a,a) -> a) }
Run Code Online (Sandbox Code Playgroud)

这需要您显式地包装和解包newtype构造函数,但是否则可以正常传递这样的多态函数.

不幸的是,我预见到这种情况会出现另一个问 不同的Random a实例使用它们的随机生成器不同的量,并且在例如Integer生成的用于构建Integer结果的原始随机数的量的情况下甚至将取决于范围的大小.因此,如果g不知道实际调用函数时使用的类型和范围,则无法获得下一个.

幸运的是,System.RandomAPI中的一个函数可以解决这个问题:split为您提供一个新的随机生成器,当您确实需要完全独立地生成多个随机值时,可以将其传递给子计算.

  • @leftaroundabout [Simon PJ的解释](https://mail.haskell.org/pipermail/glasgow-haskell-users/2014-February/024684.html) (2认同)
  • @leftaroundabout,我个人不能,但我的印象是,这是一个"深层"问题.GHC的类型检查器显然基本上无法正确执行,没有人知道如何编写一个正确的类型检查器,提供所有其他漂亮的东西,并提供模糊的人类可读的错误消息. (2认同)