如何进入内部 Maybe monad 从纯脚本中的 html 按钮中提取 class 名称?
How to get to inner Maybe monad to extract class name from html button in purescript?
我正在尝试学习 purescript
。
我在某些 HTML 中有一个按钮,我正在尝试打印其 class 名称。我正在构建 browserifying
使用 pulp
。
我使用的函数是querySelector:
import Web.DOM.ParentNode (querySelector)
这个returns我想要的项目,Element,在两个"boxes"内:一个外部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
对于 可能的元素 我认为它看起来像:
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" 等类型冲突的泥潭
是的,我承认我在这里遇到了一些问题,这并不是一个真正的初学者问题,但让我感到恼火的是获得 class 名称这样简单的事情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 层深的值?
基于我在 this video 中看到的一些类似代码,我能够得到编译的东西。我并不是说这是最好的方法,甚至不是一个好方法,只是它有效:
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
case mbtn of
Nothing -> log "mbtn failed"
Just btn -> do
let mhtmlEl = fromElement btn
case mhtmlEl of
Nothing -> log "mhtml failed"
Just htmlEl -> do
let cn = className htmlEl
log $ "classname below:"
cn >>= log
pure unit
pure unit
pure unit
pure unit
某些变量名("mbtn"、"mhtmlEl")上的 "m" 大概是表示它是一个包装对象,即单子。重要的是,在 case Just
语句中指定的变量不同于 case of
语句中的变量,前面没有 "m" 的变量("btn"、"htmlEl"),表示它是原始值或未包装值。
pure unit
行本质上是虚拟行,以满足 monad 总是 returns 除了 "binder"(let 或 '<-' 赋值)之外的东西的要求,所以您可能需要也可能不需要它们,具体取决于您的 do 工作流程的构建方式。
在这个例子中实际上有两个级别的 Maybe
:一个在 querySelector
结果上,一个在 fromElement
结果上。添加另一个 do
来处理原始 HTMLElement
,您最多可以达到三个级别的 do
。
不幸的是,querySelector
似乎为我返回 Nothing
,但这是一个正交问题。这就是打印 className 的全部动机——确定它是否找到了元素。
实际上你自己调查这个问题做得很好,我可以根据你提供的例子建议重构。
假设我们不关心在特定情况下记录失败消息。这意味着,我们可以用 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
即我们想在 btn
值 Just btn
上应用 fromElement
函数,如果它是 Nothing
则什么都不做。结果类型必须是 Maybe
值。
首先想到的是使用map
函数,但是fromElement
的类型是fromElement :: Element -> Maybe HTMLElement
,所以结果类型会是Maybe (Maybe a))
.
无论如何,我们甚至可以search such a function by type and the first result is 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
我正在尝试学习 purescript
。
我在某些 HTML 中有一个按钮,我正在尝试打印其 class 名称。我正在构建 browserifying
使用 pulp
。
我使用的函数是querySelector:
import Web.DOM.ParentNode (querySelector)
这个returns我想要的项目,Element,在两个"boxes"内:一个外部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
对于 可能的元素 我认为它看起来像:
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" 等类型冲突的泥潭
是的,我承认我在这里遇到了一些问题,这并不是一个真正的初学者问题,但让我感到恼火的是获得 class 名称这样简单的事情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 层深的值?
基于我在 this video 中看到的一些类似代码,我能够得到编译的东西。我并不是说这是最好的方法,甚至不是一个好方法,只是它有效:
getBtn :: Effect Unit
getBtn = do
log "now in getBtn"
doc <- map toParentNode (window >>= document)
mbtn <- querySelector (wrap "#btn") doc
case mbtn of
Nothing -> log "mbtn failed"
Just btn -> do
let mhtmlEl = fromElement btn
case mhtmlEl of
Nothing -> log "mhtml failed"
Just htmlEl -> do
let cn = className htmlEl
log $ "classname below:"
cn >>= log
pure unit
pure unit
pure unit
pure unit
某些变量名("mbtn"、"mhtmlEl")上的 "m" 大概是表示它是一个包装对象,即单子。重要的是,在 case Just
语句中指定的变量不同于 case of
语句中的变量,前面没有 "m" 的变量("btn"、"htmlEl"),表示它是原始值或未包装值。
pure unit
行本质上是虚拟行,以满足 monad 总是 returns 除了 "binder"(let 或 '<-' 赋值)之外的东西的要求,所以您可能需要也可能不需要它们,具体取决于您的 do 工作流程的构建方式。
在这个例子中实际上有两个级别的 Maybe
:一个在 querySelector
结果上,一个在 fromElement
结果上。添加另一个 do
来处理原始 HTMLElement
,您最多可以达到三个级别的 do
。
不幸的是,querySelector
似乎为我返回 Nothing
,但这是一个正交问题。这就是打印 className 的全部动机——确定它是否找到了元素。
实际上你自己调查这个问题做得很好,我可以根据你提供的例子建议重构。
假设我们不关心在特定情况下记录失败消息。这意味着,我们可以用 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
即我们想在 btn
值 Just btn
上应用 fromElement
函数,如果它是 Nothing
则什么都不做。结果类型必须是 Maybe
值。
首先想到的是使用map
函数,但是fromElement
的类型是fromElement :: Element -> Maybe HTMLElement
,所以结果类型会是Maybe (Maybe a))
.
无论如何,我们甚至可以search such a function by type and the first result is 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