- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
抱歉,如果这是一个常见问题。我有这个简单的 IO() 函数:
greeter :: IO()
greeter = do
putStr "What's your name? "
name <- getLine
putStrLn $ "Hi, " ++ name
现在我想调用greeter
,同时指定一个参数来预填充getLine
,这样我实际上不需要交互。我想象像一个函数
IOwithinputs :: [String] -> IO() -> IO()
那我就这么做
IOwithinputs ["Buddy"] greeter
这将产生一个不需要用户输入的 IO
操作,如下所示:
What's your name?
Hi, Buddy
我想在不修改原始IO()
函数greeter
的情况下执行此操作。我也不想编译greeter
并从命令行通过管道输入。我在 Hoogle 中没有看到类似 IOwithinputs 的东西。 (withArgs
的输入和命名都很诱人,但根本不是我想要的。)有没有一种简单的方法可以做到这一点?或者由于某种原因这是不可能的?这就是 Pipes 的用途吗?
最佳答案
正如其他人所指出的,如果您已经在使用 getLine
和 putStrLn
之类的东西,就没有干净的方法来“模拟”IO
。您必须修改greeter
。您可以使用 hGetLine
和 hPutStr
版本,并使用假的 Handle
模拟 IO
,或者您可以使用Purify Code with Free Monads方法。
它要复杂得多,但也更通用,通常很适合这种模拟,特别是当它变得更复杂时。我将在下面简要解释它,尽管细节有些复杂。
这个想法是,您将创建自己的“假 IO”单子(monad),它可以通过多种方式“解释”。主要解释是仅使用常规IO
。模拟解释用一些假行替换 getLine
并将所有内容回显到 stdout
。
我们将使用免费
包。第一步是使用仿函数描述您的接口(interface)。基本概念是每个命令都是仿函数数据类型的一个分支,并且仿函数的“槽”代表“下一个操作”。
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Trans.Free
data FakeIOF next = PutStr String next
| GetLine (String -> next)
deriving Functor
如果忽略下一步操作,从构建 FakeIOF
的人的角度来看,这些构造函数几乎就像常规的 IO
函数一样。如果我们想要PutStr
,我们必须提供一个String
。如果我们想要GetLine
,我们提供一个函数,仅在给定String
时给出下一个操作。
现在我们需要一些令人困惑的样板文件。我们使用 liftF 函数将仿函数转换为 FreeT monad。请注意,我们提供 ()
作为 PutStr
上的下一个操作,并提供 id
作为我们的 String -> next
函数。事实证明,如果我们考虑 FakeIO
Monad
的行为方式,这些就会为我们提供正确的“返回值”。
-- Our FakeIO monad
type FakeIO a = FreeT FakeIOF IO a
fPutStr :: String -> FakeIO ()
fPutStr s = liftF (PutStr s ())
fGetLine :: FakeIO String
fGetLine = liftF (GetLine id)
使用这些我们可以构建我们喜欢的任何功能,并以极小的更改重写greeter
。
fPutStrLn :: String -> FakeIO ()
fPutStrLn s = fPutStr (s ++ "\n")
greeter :: FakeIO ()
greeter = do
fPutStr "What's your name? "
name <- fGetLine
fPutStrLn $ "Hi, " ++ name
这可能看起来有点神奇——我们使用 do
表示法,而不定义 Monad
实例。诀窍在于,FreeT f m
是任何 Monad
m
和 Functor
的 Monad
f`。
这完成了我们的“模拟”greeter
函数。现在我们必须以某种方式解释它,因为到目前为止我们几乎没有实现任何功能。为了编写解释器,我们使用 Control.Monad.Trans.Free
中的 iterT
函数。其完全通用的类型如下
iterT
:: (Monad m, Functor f) => (f (m a) -> m a) -> FreeT f m a -> m a
但是当我们将它应用到我们的 FakeIO
monad 时,它看起来
iterT
:: (FakeIOF (IO a) -> IO a) -> FakeIO a -> IO a
这要好得多。我们为它提供了一个函数,该函数将“下一个操作”位置(这就是它的名称)中填充有 IO
Action 的 FakeIOF
仿函数转换为普通的 IO
操作和 iterT
将发挥将 FakeIO
变成真正的 IO
的魔力。
对于我们的默认解释器来说,这非常简单。
interpretNormally :: FakeIO a -> IO a
interpretNormally = iterT go where
go (PutStr s next) = putStr s >> next -- next :: IO a
go (GetLine doNext) = getLine >>= doNext -- doNext :: String -> IO a
但我们也可以制作一个模拟解释器。我们将使用 IO 功能来存储某些状态,特别是虚假响应的循环队列。
newQ :: [a] -> IO (IORef [a])
newQ = newIORef . cycle
popQ :: IORef [a] -> IO a
popQ ref = atomicModifyIORef ref (\(a:as) -> (as, a))
interpretMocked :: [String] -> FakeIO a -> IO a
interpretMocked greetings fakeIO = do
queue <- newQ greetings
iterT (go queue) fakeIO
where
go _ (PutStr s next) = putStr s >> next
go q (GetLine getNext) = do
greeting <- popQ q -- first we pop a fresh greeting
putStrLn greeting -- then we print it
getNext greeting -- finally we pass it to the next IO action
现在我们可以测试这些函数
λ> interpretNormally greeter
What's your name? Joseph
Hi, Joseph.
λ> interpretMocked ["Jabberwocky", "Frumious"] (greeter >> greeter >> greeter)
What's your name?
Jabberwocky
Hi, Jabberwocky
What's your name?
Frumious
Hi, Frumious
What's your name?
Jabberwocky
Hi, Jabberwocky
关于haskell 将管道字符串输入 IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19953612/
在 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
我是一名优秀的程序员,十分优秀!