我正在努力学习purescript
.
我在某些HTML中有一个按钮,我正在尝试打印类名.我正在建设和browserifying
使用pulp
.
我正在使用的函数是querySelector:
import Web.DOM.ParentNode (querySelector)
Run Code Online (Sandbox Code Playgroud)
这会在两个"框"内返回我想要的项目Element:一个外部效果monad和一个嵌入的Maybe monad:
> :type querySelector
QuerySelector -> ParentNode -> Effect (Maybe Element)
Run Code Online (Sandbox Code Playgroud)
我的效果monad看起来像:
getBtn :: Effect Unit
getBtn = do
doc <- map toParentNode (window >>= document)
button <- querySelector (wrap "#btn") doc
... Need to extract class name from 'button' here
Run Code Online (Sandbox Code Playgroud)
我知道我可以通过在外部效果monad上调用bind(>> =)来到内部.我的第一个攻击计划是使用可能的功能手动取消装箱.以下是如何为简单的Int执行此操作:
> maybe 0 (\x -> x) $ Just 7
7
Run Code Online (Sandbox Code Playgroud)
对于一个可能的元素,我认为它看起来像:
unboxMaybeElement :: Maybe Element -> Element
unboxMaybeElement Nothing = Nothing
unboxMaybeElement (Just a) = maybe (Element ..note: trying to create a default element here) (\x -> x) a
Run Code Online (Sandbox Code Playgroud)
一个问题是我找不到类型Element的构造函数,所以我无法提供默认值(也许是第一个arg ),所以我不能使用这个方法.
另外,我找不到Element类型的定义.
我还读到,无论如何都要进入一个可能的内部并不是一个好主意,而是通过使用仿函数(如地图或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)
Run Code Online (Sandbox Code Playgroud)
注意:在Web.HTML.HTMLElement中提供了className和fromElement.
正如你所看到的,我试图通过用地图调用函数来提升函数,因为函数不处理Maybes,但我陷入了"Maybe Element"和"Element"之间的类型冲突的困境中"和"HTMLElement"与"元素"等
是的,我承认我在这里遇到了一些问题,这不是一个真正的初学者问题,但它让我感到非常困难,因为获取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>
Run Code Online (Sandbox Code Playgroud)
更普遍的问题是:如何深入嵌套两个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
Run Code Online (Sandbox Code Playgroud)
然后我们可以在这里注意到一个有趣的模式:
case mbtn of
Nothing -> pure unit
Just btn -> do
let mhtmlEl = fromElement btn
Run Code Online (Sandbox Code Playgroud)
即我们想要fromElement
对一个值应用函数,btn
如果它是 ,Just btn
或者如果它是 ,则不执行任何操作Nothing
。结果类型必须是Maybe
值。
第一个想法是使用map
function,但是 的类型fromElement
是fromElement :: Element -> Maybe HTMLElement
,所以结果类型将是Maybe (Maybe a))
。
不管怎样,我们甚至可以按类型搜索这样的函数,第一个结果是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
Run Code Online (Sandbox Code Playgroud)
下一步是减少另一个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
Run Code Online (Sandbox Code Playgroud)
函数的结果类型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
Run Code Online (Sandbox Code Playgroud)