来自Haskell的State Monad的ID
可能重复:
在Haskell中创建独特的标签
我有一个数据类型Person和一些输入数据,我将从中创建Person。
我想让每个人都有自己的ID(比方说整数[0 ..])。 我可以用递归来做到这一点,但是因为我在Haskell中这样做,所以我想了解单子。 我想,Monad可能是这项工作的最佳选择。
事情是,我不是很懂很多东西:我什么时候进入monad(哪些函数可以使用内部函数),如何将它们放在一起,如何使'tick'函数前进等等。 。
所以我现在坚持这个:滴答功能可能有效,但我不知道如何使用它; 如何继续为人的建设创造价值。
import Control.Monad.State
data Person = Person {
id :: Int,
name :: String
} deriving Show
type MyState = Int
startState = 0
tick :: State MyState Int
tick = do
n <- get
put (n+1)
return n
names = ["Adam","Barney","Charlie"]
-- ??? -> persons = [Person 0 "Adam", Person 1 "Barney", Person 2 "Charlie"]
main = do
print $ evalState tick startState
-- ???
编辑:使用Data.Unique或Data.Unique.Id会更容易吗? 它将如何用于我的情况?
那么,我认为最好的解释方法就是写一些代码。
首先,你也想隐藏你当前工作的monad的内部工作。 我们将使用类型别名来做到这一点,但是有更强大的方法,请参阅Real World Haskell的本章。
type PersonManagement = State Int
之所以这样做,是因为你以后会在PersonManagement中添加更多的东西,以及使用黑匣子抽象的好习惯。
与PersonManagement的定义一起,您应该公开定义此monad的基本操作。 在你的情况下,我们现在只有滴答功能,看起来几乎相同,但签名更清晰,名称更具提示性。
generatePersonId :: PersonManagement Int
generatePersonId = do
n <- get
put (n+1)
return n
现在,所有上述应该驻留在一个单独的模块中。 除此之外,我们可以定义更复杂的操作,比如创建一个新的Person:
createPerson :: String -> PersonManagement Person
createPerson name = do
id <- generatePersonId
return $ Person id name
到目前为止,您可能已经意识到PersonManagement是一种计算类型,或者是封装用于处理Persons和PersonManagement Person
逻辑的进程PersonManagement Person
是一种计算,我们可以从中获取PersonManagement Person
对象。 这非常好,但我们如何才能真正获得我们刚刚创建的人员并对他们做些什么,例如在控制台上打印他们的数据。 那么,我们需要一个“运行”方法,它运行我们的过程并给出结果。
runPersonManagement :: PersonManagement a -> a
runPersonManagement m = evalState m startState
runPersonManagement运行monad并在后台执行所有副作用时获得最终结果(在你的情况下,勾选Int状态)。 这使用来自状态monad的evalState,它也应该驻留在上面描述的模块中,因为它知道monad的内部工作方式。 我假定,你总是想从一个固定的值开始person id,由startState标识。
例如,如果我们想创建两个人并将它们打印到控制台,程序将如下所示:
work :: PersonManagement (Person, Person)
work = do
john <- createPerson "John"
steve <- createPerson "Steve"
return (john, steve)
main = do
let (john, steve) = runPersonManagement work
putStrLn $ show john
putStrLn $ show steve
输出:
Person {id = 0, name = "John"}
Person {id = 1, name = "Steve"}
由于PersonManagement是一个完整的monad,因此您还可以使用Control.Monad中的通用函数。 假设您想从名称列表中创建人员列表。 那么,这只是monad域中的地图函数 - 它被称为mapM。
createFromNames :: [String] -> PersonManagement [Person]
createFromNames names = mapM createPerson names
用法:
runPersonManagement $ createFromNames ["Alice", "Bob", "Mike"] =>
[
Person {id = 0, name = "Alice"},
Person {id = 1, name = "Bob"},
Person {id = 2, name = "Mike"}
]
例子可以继续。
要回答你的一个问题 - 只有当你需要由monad提供的服务时,你才能在PersonManagement monad中工作 - 在这种情况下,需要函数generatePersonId,或者需要函数,而函数又需要monad的原语,比如需要createPerson
函数的work
反过来需要在PersonManagement monad中运行,因为它需要自增计数器。 例如,如果您有一个检查两个人是否具有相同数据的函数,那么您不需要在PersonManagement monad中工作,它应该是Person -> Person -> Bool
类型的普通纯函数。
要真正理解如何使用monads,你只需要通过大量的例子。 真实世界Haskell是一个很好的开始,学习你也是一个Haskell。
您还应该查看一些使用monad的库,了解它们是如何制作的以及人们如何使用它们。 一个很好的例子是解析器,parsec是一个很好的开始。
此外,P. Wadler撰写的这篇论文提供了一些非常好的例子,当然,还有更多的资源可以被发现。
在单子do
在很多方面语法的工作相当“就像你所期望的”,把整个事情,如果它是命令式语言。
那么从程序上讲,我们想在这里做什么? 迭代给定的名字,对吧? 怎么样
forM names
与来自Control.Monad
forM
。 就像你知道的那样,这几乎就像一个for
循环。 好的,首先我们需要将每个名称绑定到一个变量
forM names $ thisName -> do
我们想要做什么? 我们需要一个ID, tick
会为我们生成它
newId <- tick
并将其与该人的姓名结合起来。 就是这样!
return $ Person newId thisName
整个事情看起来像这样:
(persons, lastId) = (`runState` startState) $ do
forM names $ thisName -> do
newId <- tick
return $ Person newId thisName
如预期那样工作,或者如果Ideone安装了mtl软件包,则会...
更好地处理mapAccumL
类的问题
getPersons = snd . mapAccumL f 0
where
f n name = (n+1,Person n name)
无论如何,我修改了你的程序,使它与状态monad
import Control.Monad.State
data Person = Person {
id :: Int,
name :: String
} deriving Show
type MyState = Int
startState = 0
tick :: State MyState Int
tick = do
n <- get
put (n+1)
return n
getPerson :: String -> State MyState Person
getPerson ps = do
n <- tick
return (Person n ps)
names = ["Adam","Barney","Charlie"]
getPersonsExample :: State MyState [Person]
getPersonsExample = do
a <- getPerson "Adam"
b <- getPerson "Barney"
c <- getPerson "Charlie"
return ([a,b,c])
main1 = do
print $ evalState (sequence $ map getPerson names) startState
main2 = do
print $ evalState getPersonsExample startState
链接地址: http://www.djcxy.com/p/42913.html
上一篇: IDs from State Monad in Haskell
下一篇: State Monad, sequences of random numbers and monadic code