- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
免责声明:我对 Haskell 的无知几乎是完美的。对不起,如果这真的很基本,但我找不到答案,甚至找不到这样的问题。另外我的英文也不是很好。
据我所知,如果我在系统中有一个函数以某种方式与文件系统交互,这个函数必须使用 IO monad,并且类型会像 IO ()
在我(仅面向业务的)经验中,系统通常与文件系统交互以读取/写入包含业务数据的文件,以及记录日志。
而在商业应用中,日志无处不在。因此,如果我在 Haskell 中编写一个系统(我很长一段时间不会这样做),几乎每个函数都会使用 IO monad。
这是常见的做法还是以某种方式记录不需要IO()?或者 Haskell 业务应用程序不会记录那么多?
另外,其他类型的 I/O 怎么样?如果我需要从函数访问数据库或 Web 服务,该函数也使用 IO monad 还是 Haskell 也有 WS 和 DB monad?我几乎可以肯定只有一个 IO monad ......从我的角度来看,能够知道类型的 IO 看起来很棒,但我确定我的观点不是一个客观的衡量标准用处...
最佳答案
组织 Haskell 程序的一种典型方法是构建一个特定于应用程序的 monad,用于管理应用程序域所需的效果。这可以通过在我们所谓的“monad 转换器堆栈”中分层所需的功能来完成。如果 IO 在应用程序中无处不在,那么 IO 可以具体指定为堆栈的基础(这是它唯一可以容纳的地方,因为它是唯一不能被用户级代码解构的 monad),但是基础 monad 通常可以保持抽象,这意味着您可以使用非 IO monad 将其实例化以进行测试。
更具体地说,monad 转换器堆栈通常使用一组称为 Reader、Writer 和 State 的标准转换器构建。每个都提供了一个不同的“效果”,通过用该 monad 编写的代码隐式线程化。出于日志记录的目的,经常使用 Writer monad(以其转换器形式,WriterT);它本质上是您提供的 Monoid,它根据对其 tell
的调用建立一些输出。方法。如果你实现了基于tell
的日志功能,那么你的应用程序 monad 中的任何函数都可以有一条日志消息 mappend
编辑到日志输出。 Reader 功能通常用于通过 ask
提供一组固定的环境参数。方法;状态比较明显;它通过您的应用程序线程一些可转换的数据类型,并允许您的应用程序通过 get
转换它。和 put
方法。其他 monad 转换器也由库提供;可以为您的应用程序提供类似异常的功能,ListT 可以通过 List monad 等提供非确定性。
使用这样的转换器堆栈,您通常希望将其限制在程序的“应用程序逻辑”层中,这样您的应用程序 monad 中就不需要任何不需要效果的函数。常规模块化编程实践适用;保持您的抽象松散耦合和高度内聚,并通过普通的纯函数在其中提供功能,以便应用程序逻辑可以在高级抽象上对它们进行操作。例如,如果您的业务逻辑中有一个 Person 的概念,您将在一个对您的应用程序 monad 一无所知的 Person 模块中实现有关 Person 的数据和函数。只需使其可能失败的函数返回一个具有足够信息的值来创建日志条目;当您的应用程序逻辑使用这些函数操作 Person 值时,它可以对结果进行模式匹配,或者如果您需要组合多个可能失败的函数,则它可以在运行中的 EachT monad 中工作。然后,您可以在应用程序层中使用基于 Writer 的日志记录功能。
每个 Haskell 程序的顶层总是在 IO monad 中。如果您已经将堆栈相对于其基础 Monad 抽象化,或者只是将其完全纯粹化,那么您将需要一个小的顶层来提供您的应用程序所需的 IO 功能。如果您的应用程序 monad 是交互式的,您可以一次运行一个步骤,在这种情况下,您可以解压缩 Writer 以获取日志条目以及有关应用程序逻辑请求的其他 IO 操作的信息。结果可以通过 Reader 或 State 层反馈到应用程序环境中。如果您的应用程序只是一个批处理器,您可能只通过 IO 操作的结果提供必要的输入,运行应用程序 monad,然后通过 IO 从 Writer 转储日志。
所有这一切的目的是为了说明 monad 和 monad 转换器允许您以非常干净的方式分离现实世界应用程序的各个部分,以便您可以在大多数地方充分利用纯粹、简单的函数进行数据转换,并留下您的代码非常干净和可测试。您可以在一个小的“运行时支持”层中隔离 IO 操作,在围绕(可能是纯的)monad 转换器堆栈构建的类似但更大的层中隔离特定于应用程序的逻辑,以及在一组不依赖的模块中进行业务数据操作使用它们的应用程序逻辑的任何功能。这使您可以在以后轻松地将这些模块重新用于类似域中的应用程序。
在 Haskell 中掌握程序结构的窍门需要练习(就像在任何语言中一样),但我想你会发现在阅读了一些 Haskell 应用程序并编写了一些你自己的应用程序后,它提供的功能允许你构建非常结构良好的应用程序,非常容易扩展和重构。祝你的努力好运!
关于haskell - Haskell 能否区分不同类型的 IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24700714/
在 Haskell 中,类型声明使用双冒号,即 (::),如 not::Bool -> Bool。 但是在许多语法与 Haskell 类似的语言中,例如榆树、 Agda 、他们使用单个冒号(:)来声明
insertST :: StateDecoder -> SomeState -> Update SomeState SomeThing insertST stDecoder st = ... Stat
如果这个问题有点含糊,请提前道歉。这是一些周末白日梦的结果。 借助 Haskell 出色的类型系统,将数学(尤其是代数)结构表达为类型类是非常令人愉快的。我的意思是,看看 numeric-prelud
我有需要每 5 分钟执行一次的小程序。 目前,我有执行该任务的 shell 脚本,但我想通过 CLI 中的键为用户提供无需其他脚本即可运行它的能力。 实现这一目标的最佳方法是什么? 最佳答案 我想你会
RWH 面世已经有一段时间了(将近 3 年)。在在线跟踪这本书的渐进式写作之后,我渴望获得我的副本(我认为这是写书的最佳方式之一。)在所有相当学术性的论文中,作为一个 haskell 学生,读起来多么
一个经典的编程练习是用 Lisp/Scheme 编写一个 Lisp/Scheme 解释器。可以利用完整语言的力量来为该语言的子集生成解释器。 Haskell 有类似的练习吗?我想使用 Haskell
以下摘自' Learn You a Haskell ' 表示 f 在函数中用作“值的类型”。 这是什么意思?即“值的类型”是什么意思? Int 是“值的类型”,对吗?但是 Maybe 不是“值的类型”
现在我正在尝试创建一个基本函数,用于删除句子中的所有空格或逗号。 stringToIntList :: [Char] -> [Char] stringToIntList inpt = [ a | a
我是 Haskell 的新手,对模式匹配有疑问。这是代码的高度简化版本: data Value = MyBool Bool | MyInt Integer codeDuplicate1 :: Valu
如何解释这个表达式? :t (+) (+3) (*100) 自 和 具有相同的优先级并且是左结合的。我认为这与 ((+) (+3)) (*100) 相同.但是,我不知道它的作用。在 Learn
这怎么行 > (* 30) 4 120 但这不是 > * 30 40 error: parse error on input ‘*’ 最佳答案 (* 30) 是一个 section,它仍然将 * 视为
我想创建一个函数,删除满足第二个参数中给定谓词的第一个元素。像这样: removeFirst "abab" ( 'b') = "abab" removeFirst [1,2,3,4] even =
Context : def fib(n): if n aand returns a memoized version of the same function. The trick is t
我明白惰性求值是什么,它是如何工作的以及它有什么优势,但是你能解释一下 Haskell 中什么是严格求值吗?我似乎找不到太多关于它的信息,因为惰性评估是最著名的。 他们各自的优势是什么。什么时候真正使
digits :: Int -> [Int] digits n = reverse (x) where x | n digits 1234 = [3,1,2,4]
我在 F# 中有以下代码(来自一本书) open System.Collections.Generic type Table = abstract Item : 'T -> 'U with ge
我对 Haskell 比较陌生,过去几周一直在尝试学习它,但一直停留在过滤器和谓词上,我希望能得到帮助以帮助理解。 我遇到了一个问题,我有一个元组列表。每个元组包含一个 (songName, song
我是 haskell 的初学者,我试图为埃拉托色尼筛法定义一个简单的函数,但它说错误: • Couldn't match expected type ‘Bool -> Bool’
我是 Haskell 语言的新手,我在使用 read 函数时遇到了一些问题。准确地说,我的理解是: read "8.2" + 3.8 应该返回 12.0,因为我们希望返回与第二个成员相同的类型。我真正
当我尝试使用真实项目来驱动它来学习 Haskell 时,我遇到了以下定义。我不明白每个参数前面的感叹号是什么意思,我的书上好像也没有提到。 data MidiMessage = MidiMessage
我是一名优秀的程序员,十分优秀!