Search code examples
haskellstate-monad

How to supply generic type for the state-monad's method


I am trying to create a function for the state monad that can receive a generic type in the a :
newtype State s a=State { runstate::s->(s,a) }

I want to bind only the first type of the runstate::s->(s,a) , and later decide what a should be :

So instead of something like :

f::Int->(Int,String)
f x | x>3 = (x, "bigger then 3")
    |otherwise =(x,"smaller then 3")

make::State Int String
make =State f

How can I accomplish :

makeFree::State Int a
makeFree=State ....

You might be wondering why would I need that. While I know the type of the state i want the result of the computation to vary.


Solution

  • It's not entirely clear what you have in mind, but based on the comments, here's a few suggestions.

    Parametrise the function

    As given, f returns (Int, String), and you can't just change that. What you can do, however, is to parametrise it:

    f' :: a -> a -> Int -> (Int, a)
    f' bigger smaller x | x > 3 = (x, bigger)
                        | otherwise = (x, smaller)
    

    This variation no longer returns (Int, String), but rather (Int, a). The price you pay, however, is that you must supply bigger and smaller as arguments. We'll return to that in a moment, but before we do that, we can turn any function with the general type s -> (s, a) into a State s a value:

    make' :: (s -> (s, a)) -> State s a
    make' fn = State fn
    

    You can now partially apply f' to vary the type:

    *Q56181862> :t make' $ f' "bigger than 3" "smaller than 3"
    make' $ f' "bigger than 3" "smaller than 3" :: State Int [Char]
    *Q56181862> :t make' $ f' True False
    make' $ f' True False :: State Int Bool
    

    In the first of the above GHCi examples, the type is State Int String, whereas the second example has the type State Int Bool.

    Return

    From other comments, it seems that you wish to vary between State Int String and State Int (IO String). While you can achieve that with the above technique, you can also use return in the function itself:

    f'' :: Monad m => Int -> (Int, m String)
    f'' x | x > 3 = (x, return "bigger than 3")
          | otherwise = (x, return "smaller than 3")
    

    This varies only the monad, but not the 'return type' String. You can provide enough hints to Haskell's type system to inform it that m should be IO:

    *Q56181862> :t make' $ f'' :: State Int (IO String)
    make' $ f'' :: State Int (IO String) :: State Int (IO String)
    

    If you don't want to run the computation in IO, you can instead run it in Identity, which is also a Monad:

    *Q56181862 Control.Monad.Identity> :t make' $ f'' :: State Int (Identity String)
    make' $ f'' :: State Int (Identity String) :: State Int (Identity String)
    

    You can now run the computation and pull the String out of Identity using runIdentity.

    Functor

    If you make State s a functor as well, you can pull the String out of Identity:

    *Q56181862 Control.Monad.Identity> :t fmap runIdentity $ make' $ f''
    fmap runIdentity $ make' $ f'' :: State Int String
    

    The easiest way to do that is to use the DeriveFunctor GHC extension:

    newtype State s a = State { runstate :: s -> (s, a) } deriving Functor
    

    ...or, you could just use Control.Monad.State.Lazy from the mtl package...