来自Haskell的State Monad的ID [英] IDs from State Monad in Haskell

查看:116
本文介绍了来自Haskell的State Monad的ID的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能存在重复:

在Haskell中创建独特的标签


我有一个datatype Person和一些输入数据,我将从中创建Person。



我希望每个Person都有自己的ID(比如说整数[0 ..] )。我可以用递归来做到这一点,但是因为我在Haskell中这样做,所以我想了解单子。我想,State Monad可能是这份工作的最佳选择。



事情是,我并不真正了解很多事情:我什么时候进入monad(什么函数可以在里面使用),我如何将它们放在一起,我如何使打勾函数前进,等等......

所以我目前坚持这个:滴答功能可能有效,但我不知道如何使用它;以及如何继续为建设人员创造价值。

  import Control.Monad.State 

data Person = Person {
id :: Int,
name :: String
}派生Show

MyState = Int
startState = 0

tick :: State MyState Int
tick = do
n< - get
put(n + 1)
return n

names = [Adam,Barney,Charlie]

- ??? - > Person = [Person 0Adam,Person 1Barney,Person 2Charlie]

main = do
print $ evalState tick startState
- ???

编辑:使用Data.Unique或Data.Unique.Id可以更容易吗?它是如何用于我的情况?

解决方案

嗯,我认为最好的解释方法就是写一些代码。

首先,您还需要隐藏当前工作的monad的内部工作方式。我们将使用类型别名来做到这一点,但有更强大的方法,请参阅真实世界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是一种计算类型,或者是一个封装了用于处理Person的逻辑的进程,并且 PersonManagement Person 我们获得了一个人物。这非常好,但我们如何才能真正获得我们刚刚创建的人员并对他们做些什么,例如在控制台上打印他们的数据。那么,我们需要一个运行方法,它运行我们的过程并给出结果。

  runPersonManagement :: PersonManagement a  - > a 
runPersonManagement m = evalState m startState

runPersonManagement运行monad并获得最终结果同时在后台执行所有副作用(在你的情况下,勾选Int状态)。这使用状态monad中的 evalState ,并且它也应该驻留在上面描述的模块中,因为它知道monad的内部工作原理。我假设,你总是希望从一个固定值开始,由startState标识。



例如,如果我们想创建两个人并将其打印到控制台,程序会是这样的:

  work :: PersonManagement(Person,Person)
work = do
john< - createPersonJohn
steve< - createPersonSteve
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的基元,比如 work ,它需要 createPerson 函数,而该函数又需要在PersonManagement monad中运行,因为它需要自己增量计数器。例如,如果您有一个检查两个人是否具有相同数据的函数,则不需要在PersonManagement monad中工作,它应该是 Person - >的普通纯函数;人 - > Bool



为了真正理解如何使用monads,您只需要通过大量示例。 真实世界Haskell 是一个很好的开始,所以学习你一个Haskell



你也应该看看一些使用monad的库来看看它们是如何制作的以及人们如何使用它们。一个很好的例子是解析器, parsec 是一个很好的开始。



此外,本 < P. Wadler提供了一些非常好的例子,当然,还有更多的资源可以被发现。


Possible Duplicate:
Creating unique labels in Haskell

I've got a datatype Person and some input data from which I will create the Persons.

I'd like to have each Person have its own ID (let's say integers [0..]). I could do this with recursion, but since I'm doing this in Haskell, I'd like to understand the monads. The State Monad is probably the best for this job, I suppose?

The thing is, I don't really understand lots of things: when am I inside the monad (what functions can use the inside), how do I plumb them together, how do I make the 'tick' function advance, etc...

So I'm currently stuck with this: the tick function probably works, but I'm not sure how to use it; and how to successively get its value for the construction of Persons.

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

EDIT: would this be somehow easier with Data.Unique or Data.Unique.Id? How would it be used in my situation?

解决方案

Well, I think the best way to explain is to just write some code.

First of all, you would want too hide the inner workings of the monad in which you currently work. We'll do this with a type alias, but there are more powerful ways, see this chapter from Real World Haskell.

type PersonManagement = State Int

The reason for this is in case you'll add more things to PersonManagement later and its good practice to use the black box abstraction.

Together with the definition of PersonManagement, you should expose the primitive operations that define this monad. In your case, we have only the tick function for now which looks almost the same, but with a clearer signature and a more suggestive name.

generatePersonId :: PersonManagement Int
generatePersonId = do
    n <- get
    put (n+1)
    return n

Now, all of the above should reside in a separate module. On top of this we can define more complex operations, like the creation of a new Person:

createPerson :: String -> PersonManagement Person
createPerson name = do
    id <- generatePersonId
    return $ Person id name

By now you probably realized that PersonManagement is a type of computation, or a process which encapsulates logic for dealing with Persons and PersonManagement Person is a computation from which we obtain a person object. That's very nice, but how do we actually get the persons we just created and do something with them, like printing their data at the console. Well, we need a "run" method, which runs our process and gives us the result.

runPersonManagement :: PersonManagement a -> a
runPersonManagement m = evalState m startState

The runPersonManagement runs the monad and gets the final result while performing all the side effects in the background (in your case, ticking the Int state). This uses the evalState from the state monad, and it should also reside in the module described above since it knows about the inner workings of the monad. I assumed, that you always want to start the person id from a fixed value, identified by startState.

So for example if we wanted to create two persons and print them to the console, the program would be something like:

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

Output:

Person {id = 0, name = "John"}
Person {id = 1, name = "Steve"}

Since PersonManagement is a full fledged monad you can also use generic functions from Control.Monad for example. Let's say you want to create a list of persons from a list of names. Well, that's just the map function lifted in the domain of monads - it's called mapM.

createFromNames :: [String] -> PersonManagement [Person]
createFromNames names = mapM createPerson names

Usage:

runPersonManagement $ createFromNames ["Alice", "Bob", "Mike"] =>
    [
        Person {id = 0, name = "Alice"},
        Person {id = 1, name = "Bob"},
        Person {id = 2, name = "Mike"}
    ]

And examples could go on.

To answer one of your questions - you work in the PersonManagement monad only when you need the services provided by that monad - in this case, the generatePersonId function or you need functions which in turn require the monad's primitives like work which needs the createPerson function which in turn needs to run inside the PersonManagement monad because it needs the self-incrementing counter. If you have, for example, a function that checks whether two persons have the same data, you wouldn't need to work inside the PersonManagement monad and it should be a normal, pure function of type Person -> Person -> Bool.

To really understand how to work with monads you'll just have to go through a lot of examples. Real World Haskell is a great start and so is Learn you a Haskell.

You should also look into some libraries which use monads to see how they are made and how people use them. One great example are parsers, and parsec is a great place to start.

Also, this paper by P. Wadler provides some very nice examples and of course, there are many more resources that are ready to be discovered.

这篇关于来自Haskell的State Monad的ID的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆