(Some其他的mtl monad也会带来同样的问题,但是让我们在这里使用Reader
来解决这个问题。
假设我需要以一种人类可读的方式显示一些东西,根据用户的偏好,从配置中读取实现和行为细节:
showIt :: Config -> Object -> String
如果用户配置只是读取而不是修改,那么也可以使用Reader
来反映:
showIt :: Reader Config (Object -> String)
但是等等,这和这个有什么不同:
showIt :: Object -> Reader Config String
虽然我几乎不知道Reader
,但我确实尝试过这两种设计,它们在功能上似乎是等同的。我仍然想知道这两者之间是否存在语义上的差异,也就是说,当其他一些Haskellers查看我的代码时,如果我使用其他设计,他们会解释不同的目的或含义吗?如果是,那么在这样的场景中应该首选哪种用法?
2条答案
按热度按时间f87krz0w1#
Reader Config (Object -> String)
和Object -> Reader Config String
有什么区别?前者将
showIt
实现限制为read
a config仅一次,然后生成一个必须对所有对象都有效的字符串转换函数。后者允许实现根据输入对象决定读取什么配置(或者更确切地说,是否读取1)。它们的使用模式截然不同:
vs
1:使用
Reader
monad,没有太多的选择-您只能决定是否读取配置(或者:(我会告诉你多久读一次,但每次都是一样的。对于其他monad,阅读配置的不同位可能涉及不同的文件系统或数据库访问等,差异将变得更大。vyswwuz22#
与另一个答案相反,这些都是同一件事。
Reader a b
只是a -> b
的newtype
Package 器,这意味着
Reader a b
与a -> b
具有完全相同的功能--只是不同的API。因为a -> b -> c
只是a -> (b -> c)
的另一种写法,所以你可以看到这些都是写同一个东西的不同写法:你的最后一个例子在道德上仍然和其他例子一样。函数接受参数的顺序是无关紧要的-你可以很容易地把它写成相反的方式:
使用
Reader
的代码碰巧是以某种风格编写的(也就是说,使用do
表示法,runReader
和朋友),但这实际上只是一个分心-这是我们真正关心的行为。当然还有其他(非
Reader
)单子,这些类型是不等价的,但这是一个不同的问题!