Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unwrapping function types in Haskell

Tags:

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.

like image 986
Harvey Adcock Avatar asked Dec 14 '22 18:12

Harvey Adcock


2 Answers

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) }
like image 122
Bertie Wheen Avatar answered Dec 27 '22 06:12

Bertie Wheen


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
like image 31
bheklilr Avatar answered Dec 27 '22 08:12

bheklilr