2015-12-17 16 views
6

Ich versuche, den Typ Parameter einer State Monade in einem neuen Typ zu verstecken, aber ich habe eine harte Zeit vereinheitlichen die existent qualifizierte s mit der g für evalFoo zur Verfügung gestellt werden. Ich habe versucht mit ExistentialQuantification, GADTs und RankNTypes, aber haben ein zugegebenermaßen sehr schlechtes Verständnis davon, wie diese Erweiterungen funktionieren.Versteckt den Typ Parameter

Wie würde die idiomatische Haskell-Methode dieses Aussehen erreichen? Danke!

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

Das Ziel ist es, so etwas zu erreichen, aber in der Lage jede Instanz von RandomGen zu liefern:

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

Prelude> evalFoo myRNG (mkStdGen 123) 
0.7804356004944119 
+2

' myRNG = Foo $ state random' – user3237465

Antwort

6

Existentielle Quantifizierung in der Art des Foo Konstruktor würde für jeden Wert des Typs bedeutet, dass Foo, gibt es einige Instanz von RandomGen ist, dass es als sein Zustand verwendet. Sie möchten jedoch das Gegenteil: Sie möchten, dass bei gegebenem Wert foo :: Foo und beliebige Instanz g von RandomGen Sie können g als den Zustand der Berechnung von foo eingekapselt verwenden.

Also lassen Sie uns schreiben, dass statt:

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

Das wie erwartet verwendet werden kann:

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

geben
*Main> evalFoo myRNG (mkStdGen 123) 
0.43927189736460226 

Ja, nicht ganz 0,78;)

4

Das Problem ist ziemlich viel, wie Sie beschreiben. Sie werden nicht in der Lage sein, den existentiell verpackten Zufallssamen mit Ihrem anfänglichen Zufallssamen zu vereinigen. Der offensichtlichste Ansatz ist insgesamt die existentielle Quantifizierung fallen zu lassen, und verwenden Sie nur das:

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

ich kein Problem mit dem naheliegendste Ansatz in diesem Zusammenhang sehen kann. Wenn Sie den Seed-Typ wirklich vor dem Transformator verbergen möchten, zeigt Cactus's answer, wie es richtig gemacht wird.

In einigen anderen Kontexten einige ähnlichen existentiellen Umhüllungen können zusammen mit dem Transformator durch Einwickeln des Saatguts arbeiten:

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

Sie können ein Beispiel für etwas ähnliches in dem foldl Paket sehen.

+1

Ich denke, es gibt einen Verdienst, die Wahl von 'g' zu verbergen, da es dich zwingt, nichts Instanz-spezifisches damit zu tun. – Cactus

+0

@Cactus, was ist das für dich? – dfeuer

+0

Nun, zum einen können Sie den Zustand nicht von innen durch einen neuen Zufallsgenerator ersetzen, der aus der Luft gezogen wurde. – Cactus