0

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.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
Bercovici Adrian
  • 8,794
  • 17
  • 73
  • 152
  • 2
    But here the rsult of your comutation is a `String`: it is either `"bigger than 3"`, or `"smaller than 3"`. That result is already "free" in the sense that the bind function can change the result of each computation. So if you first "return" a string, then the second expression in the bind does not per se needs to be `String`. – Willem Van Onsem May 17 '19 at 07:48
  • Well i placed that as an example where i supplied a function with both variables fixed.Can i somehow let the result variable free? And define functions afterwards ? `Int->(Int, Bool)`, `Int->(Int,Char)` etc ? By free i mean a generic type , an `a` – Bercovici Adrian May 17 '19 at 07:51
  • 1
    given you can make the `f` free, yes. For example you could generate a function `f :: Num n => Int -> (Int, n)` where the response is thus a `Num` instance, but the specific one is free. It is however a bit "strange" that this could come up with any type. – Willem Van Onsem May 17 '19 at 07:53
  • 4
    You are essentially asking to construct a function which can return a value for any possible type. You can only achieve that using infinite recursion or using an exceptional value like `undefined`. That does not look very useful -- what are you trying to achieve? – chi May 17 '19 at 07:54
  • 1
    Couldn't you make a new Algebraic DataType or GADT which will be returned by the function (thus fixing the functions return value) but having the possibility of constructing your result depending on the actual return value? (Just a wild thought...) – Fabian Schneider May 17 '19 at 07:55
  • I wanted the second variable free since i would want it to be a `String` or a `IO String` for example. **Scenario1** : run the current state get the new state and a log `Int->(Int,String)` . **Scenario 2** : get the current state, write something to a file and get the log `Int->(Int,IO String)` – Bercovici Adrian May 17 '19 at 07:57
  • 1
    How you intend to use `makeFree`? – talex May 17 '19 at 08:03
  • i would just supply it with the desired method.I would define specialized method : `freeFunc::Int->(Int,IO String)` and then i would just call in my required scenario `makeFree freeFunc ` – Bercovici Adrian May 17 '19 at 08:04
  • 2
    So `makeFree` is accepting function as parameter? Then it is easy, just define `makeFree = State`. – talex May 17 '19 at 08:08

1 Answers1

2

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...

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736