【问题标题】:Scalaz: how to combine monads of type OptionT[State[_], T], so it won't terminate on NoneScalaz:如何组合 OptionT [State [_],T] 类型的单子,因此它不会在 None 上终止
【发布时间】:2016-12-29 20:08:28
【问题描述】:

我正在学习 monad 转换器,但在对它们进行排序时遇到了问题

我创建了一个类型OptionTBitSetState[T]

我将这种类型理解为可能会失败的有状态计算

import scalaz._, Scalaz._
import scala.collection.immutable.BitSet

type BitSetState[T] = State[BitSet, T]
type OptionTBitSetState[T] = OptionT[BitSetState, T]
object OptionTBitSetState {
  def apply[T](option : Option[T]) : OptionT[BitSetState, T] =
    OptionT[BitSetState, T](State[BitSet, Option[T]](_ -> option))

  def apply[T](state : State[BitSet, T]) : OptionT[BitSetState, T] =
    OptionT[BitSetState, T](state.map(_.some))
}

我有一个带有签名的函数step

def step(i : Int) : OptionTBitSetState[Seq[Int]]

这个函数应该:

  1. 检查状态内的BitSet是否包含参数i
    • 如果不包含:将i添加到BitSet并返回Seq(i, i*10, i*100)
    • 如果它包含:失败并显示None

函数步骤的实现:

def step(i : Int) : OptionTBitSetState[Seq[Int]] =
  for {
    usedIs <- OptionTBitSetState(get[BitSet])
    res <- OptionTBitSetState(
      Some(Seq(i, i*10, i*100)).filterNot(_ => usedIs.contains(i))
    )
    _ <- OptionTBitSetState(put(usedIs + i))
  } yield res

我想对 步骤 列表进行排序,这样当我评估这个序列时,我会得到一个选项列表。但是sequence的签名不同。我得到了一个列表选项。

例如

List(1,2,1,3).map(step).sequence.run(BitSet.empty)

返回None,但我想要的是:

List(Some(Seq(1, 10, 100)), Some(Seq(2, 20, 200)), None, Some(Seq(3, 30, 300)))

有什么办法可以结合OptionTBitSetState[T]s,所以我会得到我需要的行为?

【问题讨论】:

    标签: scala monads scalaz monad-transformers


    【解决方案1】:

    以我的拙见,您使用OptionT 使解决方案过于复杂。

    OptionT 的问题在于它希望将 monad 中的值视为存在或不存在,因此当您将单个计算“折叠”到单个状态以运行它时,任何单个故障都必须导致整个失败的事情。

    我只会使用State[BitSet,Option[Seq[Int]]。这是一个(为简单起见稍作修改)Haskell 版本,因为我不太会说 Scala。

    module Main where
    
    import Control.Monad.State
    import Data.IntSet (IntSet)
    import qualified Data.IntSet as IntSet
    import Data.Maybe (isJust)
    
    step :: Int -> State IntSet (Maybe [Int])
    step i = do
        set <- get
        if not (IntSet.member i set)
        then do
            modify $ IntSet.insert i
            return $ Just [i, i*10, i*100]
        else return Nothing
    
    run xs = filter isJust $ flip evalState IntSet.empty $ mapM step xs
    
    main = do
        let result = run [1,2,1,3]
        print result
    

    您真正想要的是mapM 或任何Scala 的等价物。然后只需运行 State 操作并删除 Nothing 值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-10
      • 1970-01-01
      • 2017-04-22
      • 2019-12-16
      • 1970-01-01
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多