当前位置: 首页 > 知识库问答 >
问题:

收集行动并在必要时尽早开展业务

傅峰
2023-03-14

我正在将一些Python代码转换为Haskell。业务逻辑相当复杂,我的Haskell代码越来越难看。

我的Python函数:

def f(some_state):
    doAction1() # e.g. send_message("Hi there!")
    if not userIsAuthenticated:
        doAction2() # e.g. send_message("Please login")
        return
    if not userHasPermission:
        doAction3() # e.g. send_message("Please sudo")
        return
    if somePredicate3:
        doAction4()
        if somePredicate4:
            doAction5()
    return

对于Haskell版本,我有两个约束:

  1. 为了保持我的代码尽可能的纯粹,我想把我所有的操作收集在一个列表中(或者如果我觉得有趣的话,一个自由单子),然后(在函数之外)执行它。在Python中,我将在函数的开头定义tmp = [],将所有操作附加到tmp(不调用它们)并返回tmp。然后,我会迭代列表来执行所有的操作(根本不是Pythonic!)。
  2. 而不是像这样被推到右边的疯狂:

-

if a
then undefined
else if b
     then undefined
     else if c
          then undefined
          else if d
               then undefined
               else undefined

基本上,我想收集一个行动列表,并在必要时提前退出,这样我就可以写下如下内容:

tell Action1
when somePredicate1 $ tell doAction2
when somePredicate2 $ tell doAction3
...

tell不是来自writer monad,而是一个函数(它不存在),它聚合了迄今为止“告诉”的所有内容(即writer monad),并在我退出时返回。

这真的感觉像是一个广义的非此即彼的单子,除了我需要保留在第一个左边之前发生的所有动作(不仅仅是左值),然后在第一个左边提前退出。我认为这个解决方案可能建立在MonadPlus或其他替代品的基础上,但我就是想不出什么好东西。基本上,一个花哨的守卫

也有可能我在过度工程中获得了太多的乐趣,我应该接受我的代码必须正确漂移。但那就不优雅了,不是吗?

编辑:为了清楚起见,我想要一个f::State类型的函数-


共有1个答案

芮安顺
2023-03-14

这听起来像是mtl控制下的投掷者的理想用法。蒙纳德。除了这样:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards  #-}

module Main where

import Control.Monad.Except

data UnlockError =
    UnlockErrorNoLogin
  | UnlockErrorNeedsSudo

data State = State {
    stateUserIsAuthenticated :: Bool
  , stateUserHasPermission :: Bool
  }

unlock :: MonadError UnlockError m => State -> m String
unlock State{..} = do
  action1
  unless stateUserIsAuthenticated
         (throwError UnlockErrorNoLogin)
  unless stateUserHasPermission
         (throwError UnlockErrorNeedsSudo)
  return "all"
  where
    action1 = return ()

main :: IO ()
main = do
  state <- conjureState
  case unlock state of
    Left UnlockErrorNoLogin ->
      p "please log in"
    Left UnlockErrorNeedsSudo ->
      p "please sudo"
    Right count ->
      p ("i love you with " ++ show count ++ " of my heart")
  where
    p =
      putStrLn
    conjureState =
      return (State True True)
 类似资料:
  • 问题内容: 我正在为Android应用程序实现缓存机制。 我使用,就像发现的许多示例一样。问题是,当我向上或向下滚动时, 大多数 图像已被清除。我在LogCat中看到,每次应用程序加载新图像时,我的应用程序都会被垃圾回收。这意味着中的 大多数 不可见图像都消失了。 因此,每次我 滚动回到 较早的位置(之前我确实下载过图像)时,都必须再次下载图像-它们 不会被 缓存 。 我也研究了这个话题。根据Ma

  • 有没有人尝试在多台计算机中并行启动Quartz作业应用程序? 我在我的应用程序中创建了Quartz集群作业,它在多台计算机中运行。石英工作良好,当我开始应用程序顺序,在所有主机一个接一个。然而,当我并行启动应用程序时,我会得到一些不同类型的错误。 例如: > 作业和触发器注册失败:null org.quartz.objectalreadyexistsexception:无法存储名称为“Trigge

  • 我有两个问题。其中一个会把话题弄得乱七八糟:) 1)我遇到了一个问题,即无法找到关于不同垃圾收集器在Hotspot中如何工作的完整信息。但我不是在谈论垃圾收集器工作的一般描述(我们在互联网上有很多这样的信息),我是在谈论具体的算法。我找到了这本白皮书(Java HotSpot虚拟机中的内存管理)http://www.oracle.com/technetwork/Java/javase/tech/m

  • 我们在几个应用程序节点上聚集了Quartz scheduler runner。应用程序节点需要更新,出于高可用性的原因,更新以滚动更新的方式进行。

  • 以下代码 由IntelliJ转换为: 可以缩短为 这两个流版本不编译。 IntelliJ,暗示值中的i存在问题[i]: 不兼容的类型 必需:int找到:java。lang.对象 编译器抱怨: 错误:(35,17)java:接口java中的方法collect。util。流动IntStream不能应用于给定类型 必需:java。util。作用供应商,爪哇。util。作用ObjIntConsumer,j

  • 在我们的kafka broker设置中,GC平均需要20毫秒,但随机增加到1-2秒。极端情况持续9秒。这种情况的发生频率相当随机。平均每天发生15次。我尝试过使用GCEasy,但没有给出任何见解。我的内存使用率为20%,但进程仍然使用交换,尽管内存可用。感谢您对如何将其最小化的任何意见 JVM选择: GC日志: