- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我决定简化我的代码,看看是什么条件产生了错误。我从一个简单的嵌套“s”开始,类似于 ST s (STArray s x y)
像这样:
{-# LANGUAGE RankNTypes #-}
import Control.Monad.ST
import Control.Applicative
data Foo s = Foo { foo::Bool }
newFoo :: ST s (Foo s)
newFoo = return $ Foo False
为了测试状态代码,我运行以下转换:
changeFoo :: (forall s. ST s (Foo s)) -> ST s (Foo s)
changeFoo sf = do
f <- sf
let f' = Foo (not $ foo f)
return f'
我想在保留状态的同时从状态中提取一个值,因此下一步是提取 Bool 值:
splitChangeFoo :: (forall s. ST s (Foo s)) -> ST s (Bool,(Foo s))
splitChangeFoo sf = do
f <- changeFoo sf
let b = foo f
return (b,f)
为了提取该 Bool,我必须使用 runST
。我的理解是,这将创建一个附加状态,我通过提供 forall s.
来指定该状态。在返回类型中:
extractFoo :: (forall s. ST s (Bool,(Foo s))) -> (forall s. (Bool,ST s ((Foo s))))
extractFoo sbf = (runST $ fst <$> sbf,snd <$> sbf)
上面的示例在没有最终 forall
的情况下进行编译但是在我尝试调试的情况下这是不可能的。无论如何,在这种情况下它都会以任何一种方式编译。
我能够使用上面的代码将状态的多个使用链接在一起:
testFoo :: (Bool, ST s (Foo s))
testFoo = (b && b',sf')
where
(b,sf) = extractFoo $ splitChangeFoo newFoo
(b',sf') = extractFoo $ splitChangeFoo sf
现在我尝试将 IO 加入其中,因此我使用了应用 fmap <$>
。这不会编译:(注意,如果我使用 fmap
或 >>= return
而不是 <$>
也会出现同样的问题)
testBar :: IO (Bool, ST s (Foo s))
testBar = (\(b,sf) -> extractFoo $ splitChangeFoo sf) <$> testBar'
where
testBar' :: IO (Bool, ST s (Foo s))
testBar' = return $ extractFoo $ splitChangeFoo newFoo
出现以下错误:
Couldn't match type `s0' with `s2'
because type variable `s2' would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: ST s2 (Foo s2)
The following variables have types that mention s0
sf :: ST s0 (Foo s0) (bound at src\Tests.hs:132:16)
Expected type: ST s2 (Foo s2)
Actual type: ST s0 (Foo s0)
In the first argument of `splitChangeFoo', namely `sf'
In the second argument of `($)', namely `splitChangeFoo sf'
In the expression: extractFoo $ splitChangeFoo sf
我从这个SO question about ST function composition知道编译器并未考虑 ST 的所有可能用途。为了测试这个假设,我修改了上面的函数,不使用 IO,而是简单地将返回值传递给 lambda:
testFooBar :: (Bool, ST s (Foo s))
testFooBar = (\(b,sf) -> extractFoo $ splitChangeFoo sf) testFooBar'
where
testFooBar' :: (Bool, ST s (Foo s))
testFooBar' = extractFoo $ splitChangeFoo newFoo
可以预见的是,这也不会在编译时出现相同的错误消息。
这提出了挑战。我有相当数量的 IO 需要与一组深度嵌套的 ST 操作进行交互。它对于单次迭代来说工作得非常好,但是我无法进一步使用返回值。
最佳答案
我不确定所有这些在你的实际代码中是如何实现的,在我看来,你正在做一些比严格必要的更复杂的事情,但由于我不能对此说太多明确的话,我会仅解决您问题中的直接问题。
testBar
不起作用在 (\(b,sf) -> extractFoo $ splitChangeFoo sf) <$> testBar'
,我们有<$>
,其类型为 forall a b. (a -> b) -> IO a -> IO b
。然而,testBar
类型为forall s. IO (Bool, ST s (Foo s))
。我们只能申请fmap
至testBar
s
之后已经实例化了。
就您而言,s
被实例化为一个新变量 s0
,所以在 lambda sf
内也有这个参数。不幸的是,s0
与最上面的 s
不同参数(我们希望在返回类型中看到),所以 sf
现在几乎无法使用。
以testFooBar
为例,问题是 lambda 参数默认是单态的(否则类型推断将变得不可判定),因此我们只需注释类型即可:
testFooBar :: (Bool, ST s (Foo s))
testFooBar =
(\(x :: forall s. (Bool, (ST s (Foo s)))) -> extractFoo $ splitChangeFoo $ snd x) testFooBar'
where
testFooBar' :: (Bool, ST s (Foo s))
testFooBar' = extractFoo $ splitChangeFoo newFoo
我们在这里所做的只是为 let
引入一个笨拙的替代方案。和where
表达式。但这没有什么意义,因为在testFoo
中fmap
要求 lambda 参数具有完全实例化的类型,因此我们无法通过注释消除我们的问题。
我们可以尝试以下方法:
{-# LANGUAGE ScopedTypeVariables #-}
testBar :: forall s. IO (Bool, ST s (Foo s))
testBar = (\(b,sf) -> extractFoo $ splitChangeFoo sf) <$> testBar'
where
testBar' :: IO (Bool, ST s (Foo s))
testBar' = return $ extractFoo $ splitChangeFoo newFoo
现在testBar
有权利 s
参数开头;由于它不再通用,我们可以应用fmap
马上到它。在 lambda 内部,sf
类型为ST s (Foo s)
。但这也行不通,因为 extractFoo
预计 forall
量化参数,和 sf
是单态的。现在 - 如果你在 GHCi 中尝试这个版本 - 我们收到的错误消息不再是关于转义 skolems,而是一个很好的老式 can't match type s with s2
。
显然,extract
必须返回 forall s.
-ed ST s (Foo s)
不知何故。你的原始类型不好,因为 GHC 没有多态返回类型,所以
(forall s. ST s (Bool, Foo s)) -> (forall s. (Bool, ST s (Foo s)))
实际上意味着
forall s. (forall s. ST s (Bool, Foo s)) -> (Bool, ST s (Foo s))
自从 GHC 浮出所有 forall
-s 到范围的顶部。这使得类型推断更加容易,并允许更多术语进行类型检查。事实上,您可以输入 :t extract
在 GHCi 中并以浮出类型呈现。
一个可能的解决方案是启用 ImprediactiveTypes
并拥有
extract :: (forall s. ST s (Bool, Foo s)) -> (Bool, forall s. ST s (Foo s))
但是 ImpredicativeTypes
是有原因的很少使用:它与类型检查机制的其余部分集成得很差,并且除了最简单的目的之外,很容易失败。例如,假设 extract
使用修改后的类型,以下示例不会进行类型检查:
testBar :: IO (Bool, forall s. ST s (Foo s))
testBar = (\(b, sf) -> (b, sf)) <$> testBar'
where
testBar' :: IO (Bool, forall s. ST s (Foo s))
testBar' = return $ extractFoo $ splitChangeFoo newFoo
但是当我们改变 (\(b, sf) -> (b, sf))
时它会进行类型检查至(\x -> x)
!所以让我们忘记命令式类型。
通常的解决方案是将量化类型包装在新类型中。这比命令式类型的灵 active 要差很多,但至少它是有效的。
{-# LANGUAGE RankNTypes #-}
import Control.Monad.ST
import Control.Applicative
import Control.Arrow
data Foo s = Foo { foo::Bool }
newtype ST' f = ST' {unST' :: forall s. ST s (f s)}
newFoo :: ST s (Foo s)
newFoo = return $ Foo False
splitChangeFoo :: ST s (Foo s) -> ST s (Bool, Foo s)
splitChangeFoo = fmap (\(Foo b) -> (b, Foo (not b)))
extract :: (forall s. ST s (Bool, Foo s)) -> (Bool, ST' Foo)
extract s = (runST $ fst <$> s, ST' $ snd <$> s)
testBar :: IO (Bool, ST s (Foo s))
testBar = (\(_, ST' s) -> second unST' $ extract $ splitChangeFoo s) <$>
(return $ extract $ splitChangeFoo newFoo)
因此,每当您需要返回量化类型时,请将其包装在新类型中,然后根据需要展开。您可能希望为自定义类型坚持通用类型参数方案(例如,始终将 s
参数放在最后,这样您就可以将单个 ST'
类型用于多个要包装的类型)。
关于haskell - 使用 ST Monad 混合 IO - "type variable ` s 2' would escape its scope",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26189725/
一个简单的GNU m4问题,但是我找不到正确的答案。我想在代码部分的开始/结束处打印一个markdown header : ``` echo Hello ``` 如何创建包含3个反引号的GNU M4宏
我有一个 shell 变量(我们将调用 x),其中包含一个带有 shell 元字符的字符串。例如,我可能有 abc "def's" ghi 设置为 x='abc "def'\''s" ghi'
我有一个 shell 变量(我们将调用 x),其中包含一个带有 shell 元字符的字符串。例如,我可能有 abc "def's" ghi 设置为 x='abc "def'\''s" ghi'
执行以下行时: df = df[df['Directory'].str.contains("C:\Windows\System32\Tasks")] 我收到以下错误: File "/Users/pat
我正在使用 python-social-auth 从我的 Django 应用程序登录社交网络。在我的本地机器上一切正常,但是当我部署到服务器时出现以下错误: oauthlib.oauth1.rfc58
URI.escape 和 CGI.escape 有什么区别,我应该使用哪一个? 最佳答案 斧头和剑有什么区别,我应该使用哪一种?好吧,这取决于您需要做什么。 URI.escape 应该将字符串 (UR
我今天安装了 Zenwalk Linux。我发现 Escape 键没有绑定(bind)到任何东西(至少据我所知),而是 Escape 通常会做什么(关闭程序,打开 vim 命令行等)Shift-Esc
我遇到过一种情况,闭包可能在函数 f1 内部调用(闭包被传递到其中),或者可能会传递给其他函数 f2 >。 所以现在我想知道应该如何定义这个闭包的转义行为。我的意思是我应该放 @escaping 还是
在 js/jQuery 中有没有办法拥有这两种按键组合? ESCape 键 和 SHIFT + ESCape 键 当我实现它时使用: document.onkeydown = function(e){
我正在尝试将字符串中的任何换行符或转义换行符规范化为转义的 unix 换行符。我不明白为什么这不起作用: Pattern EOL = Pattern.compile("(\\\\r)?\\\\n|\r
swift 4.2,Xcode 10.1 在我正在处理的订单处理应用程序中,用户可以搜索已处理或提交的订单。发生这种情况时,它将检查是否有订单缓存,如果没有,它将使用异步 API 请求重新填充该缓存,
我的几个网页名称中包含以下字符&,例如“Shipping & Deliveries”等 我的架构标记注入(inject)了 GTM (JSON-LD),但在 SDTT 中我收到以下错误: Uncate
自版本 1.9.0 起,Twig为 escape 过滤器提供 html_attr 策略(参见 documentation )。 html 策略使用 htmlspecialchars PHP 函数(通过
我有一个像这样的 View Controller : class PublicationListViewController: UIViewController { var publicati
这是我的代码: class Main { init() { let x = Sub(s: foo) } func foo(completion: @escapi
我正在尝试将闭包用于一阶谓词演算,并且我打算定义以下函数: func ASSUM(p: @escaping Pred) -> (Pred) -> Pred { return { q in AN
我有关于 ezSQL_mysql 和 ezSQLcore 的问题,它可能是 PHP 版本不兼容,我分享下面的代码。我应该使用什么版本的 PHP 或者我应该做什么来定制 mysqli? (我的PHP版本
我有以下函数,其中有完成处理程序,但出现此错误: Closure use of non-escaping parameter may allow it to escape 这是我的代码: func m
我正在使用 Swift-VectorBoolean 库,它目前在 Swift 3.2 上,尚未针对 Swift 4.2 进行更新,但应该仍可在 Xcode 10 上运行。在 Xcode 9 上运行它,
我想用这个 ansible 命令在 nrpe.cfg 中插入一个 nrpe 命令 check_tomcat_threads.pl -H localhost -p 30011 -C '"http-bio
我是一名优秀的程序员,十分优秀!