【问题标题】:How to get to inner Maybe monad to extract class name from html button in purescript?如何进入内部 Maybe monad 从纯脚本中的 html 按钮中提取类名?
【发布时间】:2018-08-22 14:00:15
【问题描述】:

我正在尝试学习purescript

我在一些 HTML 中有一个按钮,我正在尝试打印其类名。我正在使用pulp 构建和browserifying

我使用的函数是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 类型的构造函数,所以我无法提供默认值(可能的第一个参数),所以我可以' t 使用此方法。

另外,我找不到 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)

注意:classNamefromElement 在 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 级别的值?

【问题讨论】:

  • 你试过fmap,提升一个功能吗? map 对列表进行操作。
  • 是的,我意识到了这种区别并尝试过,但是对于我的生活,我在 purescript 中找不到任何 fmap 定义?它肯定不在前奏中,或 Data.Functor,或 Data.Maybe。不过,我 确实 在 ghci 中看到了 fmap。 Haskell 和 Purescript 之间存在一些差异,也许这就是其中之一?
  • 原谅我,我错过了这是纯粹的。你可以在这里看到:github.com/purescript/purescript-prelude/blob/master/src/Data/… 映射操作在纯脚本中确实被称为mapclassName 的类型签名是什么?您能否编辑您的帖子以包含您尝试解除该功能时遇到的错误?
  • 没有明确的想法,但有两个想法: 1. getClassName :: Maybe HTMLElement -&gt; String 看起来很奇怪:如果是Nothing,应该返回什么字符串? 2. 不确定fromElement 来自哪里,但我猜它也会返回Maybe?如果是这样,使用此函数映射Maybe 将添加另一个Maybe 的“层”。听起来您需要 &gt;&gt;= 或 do-notation。我的一般建议是添加类型签名并使用 typed hole 来查看编译器期望的类型。希望这会有所帮助。

标签: html purescript


【解决方案1】:

实际上,您通过自己调查此问题做得很好,我可以根据您提供的示例建议重构。

假设我们不关心在特定情况下记录失败的消息。这意味着,我们可以用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 值上应用fromElement 函数,如果它是Just btn,或者如果它是Nothing,则什么也不做。结果类型必须是Maybe 值。

首先想到的是使用map函数,但是fromElement的类型是fromElement :: Element -&gt; Maybe HTMLElement,所以得到的类型会是Maybe (Maybe a))

无论如何,我们甚至可以通过类型来search 这样的函数,第一个结果是bind(与(&gt;&gt;=) 相同)。所以让我们重构(为了清楚起见,将类型指向为 cmets,但确保没有必要):

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(同&lt;$&gt;)就足够了:

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

【讨论】:

  • 是的,这就是我一直在努力的解决方案,并且知道必须在某个地方!它向我展示了 Haskell (PureScript) 是如何真正是数学的,而函数是如何真正表达式。例如,我基本上有“y= x + x + x”,你证明它可以更简单地写成“y = 3x”。有趣的是 Haskell 如何允许这种客观且几乎是算法的重构。感谢您的精准思维课程。
【解决方案2】:

基于我在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 始终返回除“binder”(let 或 '

在此示例中,Maybe 实际上有两个级别:一个在querySelector 结果上,一个在fromElement 结果上。添加另一个do 来处理原始HTMLElement,您最多可以达到三个级别的do

不幸的是,querySelector 似乎为我返回了Nothing,但这是一个正交问题。这就是打印 className 的全部动机——以确定它是否找到了元素。

【讨论】:

    猜你喜欢
    • 2017-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    相关资源
    最近更新 更多