Harvey Adcock
Harvey Adcock

Reputation: 971

Unwrapping function types in Haskell

is there a way to unwrap the following type in Haskell?

newtype Rand a = Rand(StdGen -> (a , StdGen))

I've got a function which returns this type and another one which I'd like to use the 'a' value from the returned function, but I can't figure out how to extract the value.

Upvotes: 5

Views: 981

Answers (4)

Bertie Wheen
Bertie Wheen

Reputation: 1243

When you write:

newtype Rand a = Rand(StdGen -> (a , StdGen))

It's important to know what you're writing.

In this case, newtype is equivalent to data (the only caveat being newtype is more efficient). So, we could alternatively write the type definition like so:

data Rand a = Rand (StdGen -> (a, StdGen))

Piece by piece:

Type constructor
      |              ..-- Type parameter
     \|/       ..--''
      '   .--''
data Rand a = Rand (StdGen -> (a, StdGen))
               .   '---------------------'
              /|\              |
               |               '- Wrapped data
     Data constructor

First, let's look at a simpler example:

data Sum = Sum Int

Annotated:

Type constructor
      |
     \|/
      '
data Sum = Sum Int
            .  '-'
           /|\  '--- Wrapped data
            |
     Data constructor

To be clearer, we'll differentiate the type (constructor) from the (data) constructor:

data SumType = SumCon Int

Now, how would we extract the Int held in a value x :: SumType?

The obvious choice is pattern matching:

getSum :: SumType -> Int
getSum (SumCon n) = n

And this works. But this is a pretty common (and trivial) thing to want to do - and to make it easier, 'record syntax' was introduced. This means we can rewrite our type like so:

data SumType = SumCon { getSum :: Int }

Now we don't have to write getSum manually any more -- the compiler will do it for us, meaning we can assume a function getSum :: SumType -> Int exists.

Now, let's go back to Rand a:

newtype Rand a = Rand (StdGen -> (a, StdGen))

We could either manually write:

getRand :: Rand a -> (StdGen -> (a, StdGen))
getRand (Rand f) = f

Or just let the compiler do it for us:

newtype Rand a = Rand { getRand :: StdGen -> (a, StdGen) }

Upvotes: 9

Lee
Lee

Reputation: 144136

You can match on the Rand constructor:

getRand :: Rand a -> (StdGen -> (a, StdGen))
getRand (Rand f) = f

You need an instance StdGen to be able to get a value of a from the resulting function. You can use mkStdRandom e.g.

genWith :: Rand a -> Int -> a
genWith (Rand f) = fst . f . mkStdGen

Upvotes: 1

jamshidh
jamshidh

Reputation: 12070

Just make it a named record....

newtype Rand a = Rand{ runRandom::(StdGen -> (a , StdGen)) }

then you can get it like this

runRandom x --returns the function

or use it like this

runRandom x gen

Upvotes: 1

bheklilr
bheklilr

Reputation: 54058

When you have the data type

newtype Rand a = Rand (StdGen -> (a, StdGen))

The only way you can get an a out of it is by supplying a StdGen (remember, this is more or less an alias for StdGen -> (a, StdGen)):

runRand :: Rand a -> StdGen -> (a, StdGen)
runRand (Rand f) g = f g

To make it simpler, I would recommend instead defining Rand with record syntax:

newtype Rand a = Rand { runRand :: StdGen -> (a, StdGen) }

Where runRand will have the same type, but now it's all defined in one line for you. If you want just the a value, then just use fst:

evalRand :: Rand a -> StdGen -> a
evalRand r g = fst $ runRand r g

Upvotes: 2

Related Questions