2015-12-17 7 views
6

Я пытаюсь скрыть параметр типа монады State в новом типе, но мне сложно объединить существующий s с g, который будет предоставлен для evalFoo. Я пробовал с ExistentialQuantification, GADTs и RankNTypes, но, по общему признанию, очень плохо понимал, как работают эти расширения.Скрыть параметр типа монады монады

Как бы идиоматический способ Хаскелла выполнить этот взгляд? Спасибо!

{-# LANGUAGE GADTs #-} 

import Control.Monad.State 
import System.Random 

data Foo a where 
    Foo :: RandomGen s => State s a -> Foo a 

evalFoo :: RandomGen g => Foo a -> g -> a 
evalFoo (Foo m) g = evalState m g 

Цель состоит в том, чтобы добиться чего-то подобного, но может поставить любой экземпляр RandomGen:

myRNG :: Foo Double 
myRNG = Foo $ do 
    u <- state random 
    return u 

Prelude> evalFoo myRNG (mkStdGen 123) 
0.7804356004944119 
+2

' myRNG = Foo $ state random' – user3237465

ответ

6

Пространствен Количественные в типе Foo конструктора будет означать, что для каждого значения типа Foo, есть некоторые экземпляр RandomGen, что он использует в качестве своего состояния. Вы хотите наоборот: вы хотите, чтобы с учетом любого значения foo :: Foo и любой экземпляр g из RandomGen вы можете использовать g как состояние вычисления, инкапсулированного foo.

Так давайте писать, что вместо того, чтобы:

{-# LANGUAGE Rank2Types #-} 

import Control.Monad.State 
import System.Random 

newtype Foo a = MkFoo{ unFoo :: forall g. (RandomGen g) => State g a } 

evalFoo :: RandomGen g => Foo a -> g -> a 
evalFoo = evalState . unFoo 

Это может быть использовано, как и ожидалось:

myRNG :: Foo Double 
myRNG = MkFoo $ do 
    u <- state random 
    return u 

давая

*Main> evalFoo myRNG (mkStdGen 123) 
0.43927189736460226 

Да, не совсем 0,78;)

4

Проблема в значительной степени, как вы описываете. Вы не сможете объединить экзистенциально завернутое случайное семя с вашим начальным случайным семенем. Наиболее очевидный подход уронить квантор существования в целом, и просто использовать это:

runRandomly :: RandomGen g => State g a -> g -> a 
runRandomly (Foo m) g = evalState m g 

Я не вижу никаких проблем с наиболее очевидным подходом в этом контексте. Если вы действительно хотите скрыть тип семян от трансформатора, Cactus's answer показывает, как это сделать правильно.

В некоторых других контекстах, некоторые подобные экзистенциальные обертываний могут работать по подведению семена вместе с трансформатором:

data Foo a where 
    Foo :: RandomGen s => State s a -> s -> Foo a 

Вы можете увидеть пример что-то подобное в foldl пакете.

+1

Я думаю, что есть смысл скрывать выбор 'g', поскольку он заставляет вас не делать с ним ничего конкретного. – Cactus

+0

@ Кактус, что тебя побеждает? – dfeuer

+0

Ну, для одного, вы не можете заменить состояние, изнутри, новым случайным генератором, вытащенным из воздуха. – Cactus

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

  • Нет связанных вопросов^_^