0

有人可以解释如何将此 FSharpx stm 编写为管道吗?

    stm {
        let! allTops = readTVar tAllTops
        let! thisPlayerTops = mapM removeOtherPlayersScores allTops
        let! markedTops = mapM markAsNonEmpty thisPlayerTops

        return 
            markedTops 
            |> Seq.filter fst 
            |> Seq.map snd
            |> List.ofSeq
    } 

我正在考虑类似haskell的>>=管道。

谢谢!

更新:为了避免混淆,做一点澄清:

我在想应该能够根据 stm.Bind 和 stm.Return 在 F# 中定义 >>= 运算符。我迷路了,我试图自己做到这一点。

UPDATE2:在Thomas 的回答之后,我发布了我认为看起来还不错的更新版本。如果我理解正确,由于缺少类型类,运算符 >>= 没有与 Haskell 相同的功能。

我同意这对于 F# 来说不是惯用的,但它可能是一个很好的练习。

    readTVar tAllTops
    >>= mapM removeOtherPlayersScores 
    >>= mapM markAsNonEmpty 
    >>= stm.Return >> Seq.filter fst  >> Seq.map snd >> List.ofSeq
    |> atomically

谢谢!

4

2 回答 2

2

Haskell 中的>>=运算符只是绑定操作的符号名称,因此您可以在 F# 中将其定义为 的别名stm.Bind

let (>>=) v f = stm.Bind(v, f)

使用运算符,您可以重写代码如下:

readTVar tAllTops >>= fun allTops ->
removeOtherPlayersScores allTops >>= fun thisPlayerTops ->
mapM markAsNonEmpty thisPlayerTops >>= fun markedTops ->
  markedTops 
  |> Seq.filter fst 
  |> Seq.map snd
  |> List.ofSeq
  |> stm.Return

这当然是一件有趣的事情,也是了解 F# 中 monad 的好方法(尤其是如果你有 Haskell 背景),但它不是一种惯用的风格——F# 中的惯用风格是明确地使用计算。

这种方法的一个限制(与 Haskell 相比)是它>>=不是单子的多态性,所以你没有得到任何东西。另外,我认为人们普遍认为使用计算块更具可读性(对于 F# 开发人员)

于 2014-01-13T11:50:29.693 回答
0

使用 Haskell 将 STM 表达式转换为 IO 的主要区别,将atomically单子结果(也称为计算表达式)绑定到名称(使用<-运算符)的不同语法以及 Haskell 列表默认是惰性的,这意味着您不需要需要使用该Seq库(据我所知,它在 F# 中为您提供了惰性列表)。

atomically $ do
  allTops <- readTVar tAllTops
  thisPlayerTops <- mapM removeOtherPlayersScores allTops
  markedTops <- mapM markAsNonEmpty thisPlayerTops
  return (map snd . filter fst $ markedTops)
于 2014-01-13T08:10:52.960 回答