在 where 子句下做语句
Do statement under a where clause
我正在尝试使用 <-
绑定将 IO [String]
转换为 [String]
;但是,我需要使用 do
块在 where
语句下执行此操作,但 Haskell 一直抱怨缩进。这是代码:
decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words
| words' /= [] = block : (decompEventBlocks . drop $ (length block) words')
| otherwise = []
where
do
words' <- words
let block = (takeWhile (/="END") words')
这是什么原因?我们如何在 where
语句中使用 do
块?而且,我们有没有机会在看守面前做一些陈述?
您不能将 IO 字符串 转换为字符串。
但是,您可以将 IO String 的内容绑定到 'variable',但这仍然会导致整个计算嵌入到 IO 中。
foo = do
x <- baz -- here baz is the IO String
let x' = doStuff x
return x' -- embeds the String inside IO, as otherwise the computation would result in IO ()
回答你的问题
foo x = baz x -- x here is your 'IO String'
where
baz x = do
x' <- x
return $ doStuff x'
记住:do
-blocks 是 syntactic sugar for monadic notation。这意味着以下内容适用:
do {a; b} = a >> b
dp {a <- b; c} = b >>= \a -> c
换句话说,当使用do
-表示法时,您实际上是在产生值。这就是为什么不能在 where
语句的顶层只包含一个 do
块。
解决这个问题的方法是将函数放入 do
-block:
decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words = do
-- We unwrap the IO [String], but we keep it in the do-block,
-- because it must be kept in a monadic context!
words' <- words
let block = (takeWhile (/="END") words')
-- This is equivalent to the guards you had in your function.
-- NB return :: Monad m => a -> m a, to keep it in a monadic context!
if not $ null words'
then do
-- Since the recursion is monadic, we must bind it too:
rest <- decompEventBlocks $ return $ drop (length block) words'
return $ block : rest
else return []
要了解 monads、do
-notation、>>=
和 >>
,我强烈建议阅读 the LYAH chapters 以在尝试更多 monadic 代码之前获得更好的理解.
AJFarmar 回答的角度略有不同:您在 where
中唯一可以拥有的是声明。 do
块不是声明,它们是表达式。 IE。这与您尝试编写 where 2+5
相同。如果要在 where
中声明 block
,它 必须 是
where
// can have other declarations, even mutually recursive
block = ...
Do表示法用来写一般形式的表达式
ex :: Monad m => m t
let ex = do
{ x <- foo -- foo :: Monad m => m a, x :: a
; y <- bar x -- bar x :: Monad m => m b, y :: b
; z <- baz x y -- baz x y :: Monad m => m c, z :: c
; quux x y z -- quux x y z :: Monad m => m t
}
注意所有 m
都是相同的,并且 a
、b
、c
、...可以不同,尽管 t
最后一个 do 子表达式的类型和整个 do 表达式的类型相同。
do 符号变量被 <-
构造称为 "bound"。它们在引入时进入作用域(在 <-
的左侧)并保留在所有后续 do 子表达式的作用域中。
一个可用于任何 monad 的内置 monadic 表达式是 return :: Monad m => a -> m a
。因此 x <- return v
将 x
绑定到 v
,这样 x
将在后续的子表达式中可用,并且将具有v
.
的值
所有 do 变量都 confined 那个 do
块, 不能在它之外使用。每个变量的范围是同一 do
块中的所有代码,位于变量绑定之后/之后。
这也意味着 <-
是一个非递归绑定,因为变量不能同时出现在它的右侧和左侧:它将是两个 具有相同名称的不同 个变量,在这种情况下,右侧的变量必须在该点之上的某处建立。
这里有一些通用模式:
do { _ <- p ; _ <- q ; r } === do { p ; q ; r }
do { x <- p ; return x } === do { p } === p
do { x <- return v ; foo x } === do { foo v } === foo v
do { p ; q ; r } === do { p ; do { q ; r } }
=== do { do { p ; q } ; r }
do { x <- p ; === do { x <- p ;
y <- q x ; z <- do { y <- q x ;
return (foo x y) } return (foo x y) } ;
return z }
所有 Monad m => m a
表达式都只是表达式,因此可以是一个 if - then - else
表达式,其结果分支和替代分支都是相同的单子类型(通常是让初学者感到困惑):
do { x <- p ;
y <- if (pred x) then (foo x) else (bar x) ;
return (baz x y) }
更新: monad 的要点之一是其将效果与纯计算完全分离。一旦在一个monad中,你就不能"get out"。 Monadic计算可以使用纯计算,反之则不行。
我正在尝试使用 <-
绑定将 IO [String]
转换为 [String]
;但是,我需要使用 do
块在 where
语句下执行此操作,但 Haskell 一直抱怨缩进。这是代码:
decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words
| words' /= [] = block : (decompEventBlocks . drop $ (length block) words')
| otherwise = []
where
do
words' <- words
let block = (takeWhile (/="END") words')
这是什么原因?我们如何在 where
语句中使用 do
块?而且,我们有没有机会在看守面前做一些陈述?
您不能将 IO 字符串 转换为字符串。
但是,您可以将 IO String 的内容绑定到 'variable',但这仍然会导致整个计算嵌入到 IO 中。
foo = do
x <- baz -- here baz is the IO String
let x' = doStuff x
return x' -- embeds the String inside IO, as otherwise the computation would result in IO ()
回答你的问题
foo x = baz x -- x here is your 'IO String'
where
baz x = do
x' <- x
return $ doStuff x'
记住:do
-blocks 是 syntactic sugar for monadic notation。这意味着以下内容适用:
do {a; b} = a >> b
dp {a <- b; c} = b >>= \a -> c
换句话说,当使用do
-表示法时,您实际上是在产生值。这就是为什么不能在 where
语句的顶层只包含一个 do
块。
解决这个问题的方法是将函数放入 do
-block:
decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words = do
-- We unwrap the IO [String], but we keep it in the do-block,
-- because it must be kept in a monadic context!
words' <- words
let block = (takeWhile (/="END") words')
-- This is equivalent to the guards you had in your function.
-- NB return :: Monad m => a -> m a, to keep it in a monadic context!
if not $ null words'
then do
-- Since the recursion is monadic, we must bind it too:
rest <- decompEventBlocks $ return $ drop (length block) words'
return $ block : rest
else return []
要了解 monads、do
-notation、>>=
和 >>
,我强烈建议阅读 the LYAH chapters 以在尝试更多 monadic 代码之前获得更好的理解.
AJFarmar 回答的角度略有不同:您在 where
中唯一可以拥有的是声明。 do
块不是声明,它们是表达式。 IE。这与您尝试编写 where 2+5
相同。如果要在 where
中声明 block
,它 必须 是
where
// can have other declarations, even mutually recursive
block = ...
Do表示法用来写一般形式的表达式
ex :: Monad m => m t
let ex = do
{ x <- foo -- foo :: Monad m => m a, x :: a
; y <- bar x -- bar x :: Monad m => m b, y :: b
; z <- baz x y -- baz x y :: Monad m => m c, z :: c
; quux x y z -- quux x y z :: Monad m => m t
}
注意所有 m
都是相同的,并且 a
、b
、c
、...可以不同,尽管 t
最后一个 do 子表达式的类型和整个 do 表达式的类型相同。
do 符号变量被 <-
构造称为 "bound"。它们在引入时进入作用域(在 <-
的左侧)并保留在所有后续 do 子表达式的作用域中。
一个可用于任何 monad 的内置 monadic 表达式是 return :: Monad m => a -> m a
。因此 x <- return v
将 x
绑定到 v
,这样 x
将在后续的子表达式中可用,并且将具有v
.
所有 do 变量都 confined 那个 do
块, 不能在它之外使用。每个变量的范围是同一 do
块中的所有代码,位于变量绑定之后/之后。
这也意味着 <-
是一个非递归绑定,因为变量不能同时出现在它的右侧和左侧:它将是两个 具有相同名称的不同 个变量,在这种情况下,右侧的变量必须在该点之上的某处建立。
这里有一些通用模式:
do { _ <- p ; _ <- q ; r } === do { p ; q ; r }
do { x <- p ; return x } === do { p } === p
do { x <- return v ; foo x } === do { foo v } === foo v
do { p ; q ; r } === do { p ; do { q ; r } }
=== do { do { p ; q } ; r }
do { x <- p ; === do { x <- p ;
y <- q x ; z <- do { y <- q x ;
return (foo x y) } return (foo x y) } ;
return z }
所有 Monad m => m a
表达式都只是表达式,因此可以是一个 if - then - else
表达式,其结果分支和替代分支都是相同的单子类型(通常是让初学者感到困惑):
do { x <- p ;
y <- if (pred x) then (foo x) else (bar x) ;
return (baz x y) }
更新: monad 的要点之一是其将效果与纯计算完全分离。一旦在一个monad中,你就不能"get out"。 Monadic计算可以使用纯计算,反之则不行。