我正在尝试学习 purescript
。
我在一些 HTML 中有一个按钮,我试图打印它的类名。我正在使用 pulp
构建和浏览器
。
我使用的函数是querySelector:
import Web.DOM.ParentNode (querySelector)
这会在两个“框”中返回我想要的项目 Element:一个外部 Effect monad 和一个嵌入的 Maybe monad:
> :type querySelector
QuerySelector -> ParentNode -> Effect (Maybe Element)
我的 Effect monad 看起来像:
getBtn :: Effect Unit
getBtn = do
doc <- map toParentNode (window >>= document)
button <- querySelector (wrap "#btn") doc
... Need to extract class name from 'button' here
我知道我可以通过在外部 Effect monad 上调用 bind (>>=) 来访问内部 Maybe。我的第一个攻击计划是使用 maybe 函数手动拆箱 Maybe。以下是对简单 Int 执行此操作的方法:
> maybe 0 (\x -> x) $ Just 7
7
对于 Maybe 元素,我认为它看起来像这样:
unboxMaybeElement :: Maybe Element -> Element
unboxMaybeElement Nothing = Nothing
unboxMaybeElement (Just a) = maybe (Element ..note: trying to create a default element here) (\x -> x) a
一个问题是我找不到类型Element 的构造函数,所以我无法提供默认值(可能是 的第一个参数),所以我可以'不要用这个方法。
此外,我找不到Element 类型的定义。
我还读到,无论如何,进入 Maybe 内部并不是一个好主意,而是通过使用仿函数(如 map 或 fmap
)来提升反对它的功能。
在这种情况下,我尝试调用一个外部函数,例如:
button <- querySelector (wrap "#btn") doc
let cn = getClassName button
log $ "classname=" <> show cn
-- The basic question is how to implement this function?
getClassName :: Maybe HTMLElement -> String
getClassName e = map className (map fromElement e)
注意:className 和 fromElement 在 Web.HTML.HTMLElement 中提供。
如您所见,我试图通过使用映射调用它们来提升函数,因为这些函数不处理 Maybes,但我陷入了“Maybe Element”和“Element”以及“HTMLElement”与“Element”等之间的类型冲突。
是的,我承认我在这里遇到了一些问题,这并不是一个真正的初学者问题,但令我恼火的是,像获取 HTML 对象的类名这样简单的事情却如此困难。当然,我一定错过了什么?
顺便说一句,这是我的 html:
<!doctype html>
<html>
<body>
<script src="index.js"></script>
<p>
<input type="button" class="btn-class" value="click me" id="btn"/>
</p>
</body>
</html>
更普遍的问题是:如何获得嵌套两个 monad 层的值?
最佳答案
实际上,您自己调查了这个问题,做得很好,我可以根据您提供的示例建议重构。
假设我们不关心在特定情况下记录失败的消息。这意味着,我们可以用 pure unit
替换每个这样的日志记录。 :
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
case mbtn of
Nothing -> pure unit
Just btn -> do
let mhtmlEl = fromElement btn
case mhtmlEl of
Nothing -> pure unit
Just htmlEl -> do
let cn = className htmlEl
log $ "classname below:"
cn >>= log
然后我们可以在这里注意到一个有趣的模式:
case mbtn of
Nothing -> pure unit
Just btn -> do
let mhtmlEl = fromElement btn
即我们要申请 fromElement
在 btn
上运行如果它的值(value) Just btn
如果是 Nothing
则什么都不做.结果类型必须是 Maybe
值(value)。
第一个想到的是使用map
函数,但类型为 fromElement
是fromElement :: Element -> Maybe HTMLElement
,因此结果类型将为 Maybe (Maybe a))
.
无论如何,我们甚至可以search这样一个按类型分类的函数,第一个结果是 bind
(与 (>>=)
相同)。因此,让我们重构(为了清楚起见,将类型指向注释,但确保没有必要):
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
let mhtmlEl = fromElement =<< mbtn -- Maybe HTMLElement
case mhtmlEl of
Nothing -> pure unit
Just htmlEl -> do
let cn = className htmlEl
log $ "classname below:"
cn >>= log
下一步是减少另一个case
表达。这里使用 map (与 <$>
相同)就足够了:
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
let mhtmlEl = (fromElement =<< mbtn) -- Maybe HTMLElement
let mCn = className <$> mhtmlEl -- Maybe (Effect String)
case mCn of
Nothing -> log "no such element"
Just cn -> do
log $ "classname below:"
cn >>= log
getBtn
的结果类型功能,必须是Effect Unit
, 所以每一个案例 Maybe
值必须在这里处理。我会这样保留它,因为它很清楚,这里发生了什么。但是,如果您寻求简洁的表示,那么 maybe
函数可以在这里应用:
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
let mhtmlEl = (fromElement =<< mbtn) -- Maybe HTMLElement
let mCn = className <$> mhtmlEl -- Maybe (Effect String)
maybe (log "no such element") (\cn -> log "classname below:" *> cn >>= log) mCn
关于html - 如何进入内部 Maybe monad 从纯脚本中的 html 按钮中提取类名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51781123/