如何为状态monad的方法提供通用类型

时间:2019-05-17 07:44:25

标签: haskell state-monad

我正在尝试为可以在function中接收通用类型的状态monad创建一个a
newtype State s a=State { runstate::s->(s,a) }

我只想绑定runstate::s->(s,a)的第一种类型,然后再决定a应该是什么:

所以不是像这样的东西:

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

make::State Int String
make =State f

我如何完成:

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

您可能想知道我为什么需要它。虽然我知道state的类型,但我希望计算结果有所不同。

1 个答案:

答案 0 :(得分:3)

目前尚不清楚您的想法,但是根据评论,这里有一些建议。

参数化功能

按照给定的条件,f返回(Int, String),您不能只是更改它。但是,您可以对它进行参数化:

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

此变体不再返回(Int, String),而是返回(Int, a)。但是,您要付出的代价是必须提供biggersmaller作为参数。稍后我们将返回到该状态,但是在执行此操作之前,我们可以将常规类型为s -> (s, a)的任何函数转换为State s a的值:

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

您现在可以部分应用f'来更改类型:

*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

在上述GHCi示例的第一个示例中,类型为State Int String,而第二个示例的类型为State Int Bool

返回

从其他评论看,您似乎希望在State Int StringState Int (IO String)之间有所不同。尽管您可以通过上述技术实现此目标,但也可以在函数本身中使用return

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

这仅改变单子,而不改变“返回类型” String。您可以向Haskell的类型系统提供足够的提示,以通知它m应该为IO

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

如果您不想在IO中运行计算,则可以在Identity(也是Monad)中运行它:

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

您现在可以运行计算,并使用StringIdentityrunIdentity中拉出。

Functor

如果您也将State s用作函子,则可以将StringIdentity中拉出:

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

最简单的方法是使用DeriveFunctor GHC扩展名:

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

...或者,您可以只使用Control.Monad.State.Lazy包中的mtl ...