【问题标题】:Scala: implicitly consuming variable extracted as part of pattern matchingScala:作为模式匹配的一部分提取的隐式消费变量
【发布时间】:2021-06-16 18:30:51
【问题描述】:

给定这样的方法:

def toFun()(implicit a: Box) = a.toCircle()

据我了解,目前我们需要这样的语法:

anObject match {
  case Another(a,b,c, _) =>
   implicit val tempA = a
   toFun()
  case YetAnother(a,b,_) =>
   implicit val tempA = a
   toFun()
}

我们有更直观的语法吗?

anObject match {
  case Another(implicit a,b,c, _) =>
   toFun()
  case YetAnother(implicit a,b,_) =>
   toFun()
}

更新:

我知道我们可以这样做:

anObject match {
  case Another(a,b,c, _) =>
   toFun()(a)
  case YetAnother(a,b,_) =>
   toFun()(a)
}

但如果是这样的方法:

def toFun()(implicit a: Box, b: Size, c: Context) = a.toCircle()

将需要执行以下操作,从而违背了隐含的目的:

def main = {
  implicit context =>
    anObject match {
        case Another(a,b,c, _) =>
         toFun()(a,b, context)
        case YetAnother(a,b,_) =>
         toFun()(a,b,context)
     }
}

示例:

请注意messageContext 及其用法

class Worker extends Actor {
    def receive = {
      case m: MessageContext =>
        /**
        *
        *   Notice this
        */
        implicit val messageContext = m

        processor.processMessage{
            conversation =>
                val lastMessage = conversation.chat.last
                lastMessage.message match {
                    case Some(text) =>
                        if (text.contains("/search")) {
                            val searchArgs = text.trim.split("/search")
                            if (searchArgs.nonEmpty) {
                                val query = searchArgs.last.trim
                                system.scheduler.scheduleOnce(1800 milliseconds) {
                                    val activeContent = s"""
                                    Some content"
                                    |""".stripMargin)
                                    conversation.activeContent = activeContent
                                    processor.reply(s"""
                                    |Here are search results for "${query}":
                                    |${activeContent.mkString("\n------------\n")}
                                    |
                                    |""".stripMargin)
                                }
                                s"""Hello ${conversation.user.firstName}, we have received a search query for "${query}". We will soon respond with search results"""
                            } else {
                                s"""Hello ${conversation.user.firstName}, please enter a keyword post search command, i.e. "/search keyword"!"""
                            }
                        } else if (text.contains("/remind")) {
                            if (conversation.activeContent.isEmpty) {
                                "Please /search something first"
                            } else {
                                val remindArgs = (allCatch opt(text.split("/remind").last.trim.toInt)) // .split(" ")
                                if (remindArgs.isDefined) {     
                                    system.scheduler.scheduleOnce(5000 milliseconds) {
                                        /**
                                        *
                                        *   The above implicit is used here, if I pass it as an argument, then I will needed to pass other implciit arguments as well
                                        *   If I pass message as a standard argument, then the reply method looses it's simplciity, and please do factor in that
                                        *   we will need to call it at n number of times as the conversation tree grows
                                        */
                                        processor.reply(s"""Here's the reminder after 5 seconds for resource: ${conversation.activeContent(remindArgs.get - 1)}""")
                                    }             
                                    s"Hello ${conversation.user.firstName}, we would love to remind you about the resource: ${conversation.activeContent(remindArgs.get - 1)} "
                                } else {
                                    """Please enter valid index number, refer to search results. Use the number in begining of each search result item i.e. "/remind 1" or "/remind 2" or "/remind 5"!"""
                                }
                            }
                        } else {
                            conversation.activeTopic match {
                                case Some(topic)=>
                                    topic match {
                                        case Goal =>
                                        "Some message..."
                                        case ChangeTopic =>
                                            if (List("yes", "yeah", "yo", "yay", "y", "ya", "yepp", "why not").contains(text.toLowerCase.replace("!", ""))) {
                                                system.scheduler.scheduleOnce(5000 milliseconds) {
                                                    /**
                                                    *
                                                    *   Notice this
                                                    *   
                                                    */
                                                    processor.reply(s"""Yeah, we can change the topic""")
                                                }
                                                "Let me think if we are done here"
                                            }
                                            else {
                                            "Some message..."
                                            }
                                        case .......
                                    }
                                case _ => s"No active topic out there! Would you like to /start again?"
                            }
                        }
                    case None =>
                        conversation.activeTopic match {
                            case Some(topic)=>
                                topic match {
                                case GoalUploadPrompt =>
                                    lastMessage.raw.document match {
                                        case Some(doc) => doc.toString
                                        case None => lastMessage.raw.photo match {
                                            case Some(photo) => 
                                                "Some message..."
                                            case None => "Some message..."
                                        }
                                    }
                            case _ =>
                                "Some message..."
                            }
                            case None => "Some message...."
                        }
                }
            }(m, ws, token)
            case _ =>
        }
    }


object processor {
    def processMessage(cb: (BotConversation) => String)(implicit msg: MessageContext, ws: WSClient, token: String) = ?
}

【问题讨论】:

  • 也可以显式传递隐式参数:toFun()(a)
  • 是的,我知道一个,它在很多情况下会导致另一个问题,请查看更新。
  • 什么“另一个问题”?
  • @Dima 请看更新
  • @OldGaurd01 简短的回答是您使用的隐含错误。如果您必须从某处提取值以在调用站点指定它们,那么它们首先不应该是隐含的。因此,这里的“正确”解决方案是将这些参数明确化。它会立即解决您提到的所有问题。除此之外,您将必须命名您“手动”传递的值,我真的看不出您的变体中有任何优于向您建议的选项。

标签: scala pattern-matching


【解决方案1】:

如 cmets 中所述,您始终可以显式传递隐式参数。 这是一种可能性。

或者,先设置隐式,然后调用:

    implicit val (a,b) = foo match {
      case Something(a, b, _) => a -> b
      case Another(a, b, _) => a -> b
    }
    toFun()

或者,解决 cmets 中提出的另一种可能性:

     case foo match {
        case DoNothing => 
        case _ => 
             implicit val (a,b) = foo match {
                 case Something(a, b, _) => a -> b
                 case Another(a, b, _) => a -> b
             }
             toFun()
     }

一般来说,您可以不断提出其他复杂问题,但在某些时候应该会变得很明显,如果您必须跳过这么多圈才能隐含地提供您的值,那么也许它们不应该真的首先是隐含的?

【讨论】:

  • 这是一个有趣的模式,但在我们将模式匹配用于业务逻辑/流的分支而不仅仅是用于参数提取的情况下,它的局限性非常大。再考虑一种没有值的情况,例如case None => doNothing()
  • @OldGaurd01 你到处都是。这个问题是关于将隐式传递给函数调用。如果没有值,则没有问题。
  • 我并不是为了争论而提出一个观点,我真的很想知道其他人对此的想法。我重新阅读了问题的标题,我已经非常清楚地提到我想隐含地使用任何需要调用方法的东西,而不需要任何编码引用。我并不是要问如何传递隐式标记参数,因为作为一种折衷方案,我们已经在这样做了。
  • @OldGaurd01 我们这里有一个XY problem:不管你“想要”什么。如果你有一个实际问题要解决,你会得到一些解决方案。如果你只是为了拥有它而“想要”拥有一个特定的语言特性,你可以继续实现它:) 提交一个拉取请求,看看它是如何进行的......
猜你喜欢
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 2016-03-29
  • 2011-05-27
  • 2011-12-10
  • 1970-01-01
相关资源
最近更新 更多