我正在尝试为可以在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
的类型,但我希望计算结果有所不同。
答案 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)
。但是,您要付出的代价是必须提供bigger
和smaller
作为参数。稍后我们将返回到该状态,但是在执行此操作之前,我们可以将常规类型为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 String
和State 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)
您现在可以运行计算,并使用String
将Identity
从runIdentity
中拉出。
如果您也将State s
用作函子,则可以将String
从Identity
中拉出:
*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
...